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::JOBSET_FILE:
832 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
833 case TREE_FILE_TYPE::SEXPR_PCB:
837 can_switch_to_project =
false;
838 can_create_new_directory =
false;
839 can_open_this_directory =
false;
849 if( can_switch_to_project )
852 _(
"Close all editors, and switch to the selected project" ),
853 KiBitmap( BITMAPS::open_project ) );
854 popup_menu.AppendSeparator();
857 if( can_create_new_directory )
860 _(
"Create a New Directory" ),
KiBitmap( BITMAPS::directory ) );
863 if( can_open_this_directory )
865 if( selection.size() == 1 )
868 text =
_(
"Reveal in Finder" );
869 help_text =
_(
"Reveals the directory in a Finder window" );
871 text =
_(
"Open Directory in File Explorer" );
872 help_text =
_(
"Opens the directory in the default system file manager" );
878 text =
_(
"Reveal in Finder" );
879 help_text =
_(
"Reveals the directories in a Finder window" );
881 text =
_(
"Open Directories in File Explorer" );
882 help_text =
_(
"Opens the directories in the default system file manager" );
887 KiBitmap( BITMAPS::directory_browser ) );
892 if( selection.size() == 1 )
893 help_text =
_(
"Open the file in a Text Editor" );
895 help_text =
_(
"Open files in a Text Editor" );
901 if( run_jobs && selection.size() == 1 )
909 if( selection.size() == 1 )
911 text =
_(
"Rename File..." );
912 help_text =
_(
"Rename file" );
916 text =
_(
"Rename Files..." );
917 help_text =
_(
"Rename files" );
926 if( selection.size() == 1 )
927 help_text =
_(
"Delete the file and its content" );
929 help_text =
_(
"Delete the files and their contents" );
931 if( can_switch_to_project
932 || can_create_new_directory
933 || can_open_this_directory
937 popup_menu.AppendSeparator();
951 wxMenu* vcs_submenu =
new wxMenu();
952 wxMenu* branch_submenu =
new wxMenu();
953 wxMenuItem* vcs_menuitem =
nullptr;
956 _(
"Add Project to Version Control..." ),
957 _(
"Initialize a new repository" ) );
958 vcs_menuitem->Enable( vcs_can_init );
962 _(
"Commit changes to the local repository" ) );
963 vcs_menuitem->Enable( vcs_can_commit );
965 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PUSH,
_(
"Push" ),
966 _(
"Push committed local changes to remote repository" ) );
967 vcs_menuitem->Enable( vcs_can_push );
969 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PULL,
_(
"Pull" ),
970 _(
"Pull changes from remote repository into local" ) );
971 vcs_menuitem->Enable( vcs_can_pull );
973 vcs_submenu->AppendSeparator();
976 _(
"Commit changes to the local repository" ) );
977 vcs_menuitem->Enable( vcs_can_commit );
979 vcs_submenu->AppendSeparator();
988 for(
size_t ii = 1; ii < branchNames.size() && ii < 6; ++ii )
990 wxString msg =
_(
"Switch to branch " ) + branchNames[ii];
992 vcs_menuitem->Enable( vcs_can_switch );
996 _(
"Switch to a different branch" ) );
997 vcs_menuitem->Enable( vcs_can_switch );
1001 vcs_submenu->AppendSeparator();
1003 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Remove Version Control" ),
1004 _(
"Delete all version control files from the project directory." ) );
1005 vcs_menuitem->Enable( vcs_can_remove );
1007 popup_menu.AppendSeparator();
1008 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
1011 if( popup_menu.GetMenuItemCount() > 0 )
1012 PopupMenu( &popup_menu );
1020 if( editorname.IsEmpty() )
1022 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1030 wxString fullFileName = item_data->GetFileName();
1032 if( !fullFileName.IsEmpty() )
1034 ExecuteFile( editorname, fullFileName.wc_str(),
nullptr,
false );
1045 item_data->Delete();
1055 if( tree_data.size() != 1 )
1059 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1060 tree_data[0]->GetFileName() );
1061 wxTextEntryDialog dlg( wxGetTopLevelParent(
this ), msg,
_(
"Change filename" ), buffer );
1063 if( dlg.ShowModal() != wxID_OK )
1066 buffer = dlg.GetValue();
1067 buffer.Trim(
true );
1068 buffer.Trim(
false );
1070 if( buffer.IsEmpty() )
1073 tree_data[0]->Rename( buffer,
true );
1082 if( tree_data.size() != 1 )
1119 wxTreeItemId itemId = Event.GetItem();
1125 if( tree_data->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1129 wxTreeItemIdValue cookie;
1130 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1133 bool subdir_populated =
false;
1136 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1140 if( !itemData || itemData->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1147 wxDir dir( fileName );
1149 if( dir.IsOpened() )
1151 std::vector<wxString> projects =
getProjects( dir );
1152 wxString dir_filename;
1153 bool haveFile = dir.GetFirst( &dir_filename );
1158 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1161 haveFile = dir.GetNext( &dir_filename );
1167 subdir_populated =
true;
1176 if( subdir_populated )
1184 wxArrayTreeItemIds selection;
1185 std::vector<PROJECT_TREE_ITEM*> data;
1189 for(
auto it = selection.begin(); it != selection.end(); it++ )
1195 wxLogTrace(
traceGit, wxS(
"Null tree item returned for selection, dynamic_cast "
1200 data.push_back( item );
1219 if( prj_dir == aSubDir )
1223 wxTreeItemIdValue cookie;
1224 wxTreeItemId root_id =
m_root;
1225 std::stack<wxTreeItemId> subdirs_id;
1227 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1231 if( ! child.IsOk() )
1233 if( subdirs_id.empty() )
1240 root_id = subdirs_id.top();
1251 if( itemData && ( itemData->
GetType() == TREE_FILE_TYPE::DIRECTORY ) )
1261 subdirs_id.push( child );
1277 const wxFileName& pathModified =
event.GetPath();
1278 wxString subdir = pathModified.GetPath();
1279 wxString fn = pathModified.GetFullPath();
1281 switch( event.GetChangeType() )
1283 case wxFSW_EVENT_DELETE:
1284 case wxFSW_EVENT_CREATE:
1285 case wxFSW_EVENT_RENAME:
1289 case wxFSW_EVENT_MODIFY:
1292 case wxFSW_EVENT_ACCESS:
1299 if( !root_id.IsOk() )
1302 wxTreeItemIdValue cookie;
1303 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1305 switch( event.GetChangeType() )
1307 case wxFSW_EVENT_CREATE:
1309 wxTreeItemId newitem =
1323 case wxFSW_EVENT_DELETE:
1337 case wxFSW_EVENT_RENAME :
1339 const wxFileName& newpath =
event.GetNewPath();
1340 wxString newdir = newpath.GetPath();
1341 wxString newfn = newpath.GetFullPath();
1365 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1371 if( newitem.IsOk() )
1391#if defined( _WIN32 )
1424 fn.AssignDir( prj_dir );
1425 fn.DontFollowLink();
1439 TO_UTF8( fn.GetFullPath() ) );
1447 TO_UTF8( fn.GetFullPath() ) );
1452 if( m_TreeProject->IsEmpty() )
1456 wxTreeItemIdValue cookie;
1457 wxTreeItemId root_id = m_root;
1459 std::stack < wxTreeItemId > subdirs_id;
1461 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1462 int total_watch_count = 0;
1468 if( subdirs_id.empty() )
1474 root_id = subdirs_id.top();
1476 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1492 if( wxFileName::IsDirReadable(
path ) )
1494 fn.AssignDir(
path );
1495 m_watcher->Add( fn );
1496 total_watch_count++;
1499 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1500 subdirs_id.push( kid );
1504 kid = m_TreeProject->GetNextChild( root_id, cookie );
1511#if defined(DEBUG) && 1
1512 wxArrayString paths;
1513 m_watcher->GetWatchedPaths( &paths );
1516 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1550 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1551 wxPaintDC dc(
this );
1553 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1554 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1556 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1557 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1571 wxString dir = tree_data->
GetDir();
1575 wxLogError(
"Failed to initialize git project: project directory is empty." );
1580 git_repository* repo =
nullptr;
1581 int error = git_repository_open(&repo, dir.mb_str());
1586 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1589 _(
"The selected directory is already a git project." ) );
1590 git_repository_free( repo );
1596 error = git_repository_init( &repo, dir.mb_str(), 0 );
1600 git_repository_free( repo );
1606 git_error_last()->message );
1620 dlg.SetTitle(
_(
"Set default remote" ) );
1632 git_remote* remote =
nullptr;
1641 fullURL = dlg.
GetRepoURL().StartsWith(
"https" ) ?
"https://" :
"http://";
1649 fullURL.append( wxS(
":" ) );
1653 fullURL.append( wxS(
"@" ) );
1664 error = git_remote_create_with_fetchspec( &remote, repo,
"origin",
1665 fullURL.ToStdString().c_str(),
1666 "+refs/heads/*:refs/remotes/origin/*" );
1668 if( error != GIT_OK )
1674 git_error_last()->message );
1690 _(
"Fetching Remote" ),
1729 _(
"Fetching Remote" ),
1732 if( handler.
PerformPull() != PullResult::Success )
1756 _(
"Fetching Remote" ),
1759 if( handler.
PerformPush() != PushResult::Success )
1772 if(
int error = git_reference_name_to_id( &head_oid, aRepo,
"HEAD" ) != 0 )
1774 wxLogError(
"Failed to lookup HEAD reference" );
1779 git_commit* commit =
nullptr;
1780 if(
int error = git_commit_lookup( &commit, aRepo, &head_oid ) != GIT_OK )
1782 wxLogError(
"Failed to lookup commit" );
1786 git_reference* branchRef =
nullptr;
1788 if( git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ) != 0 )
1790 wxLogError(
"Failed to create branch" );
1791 git_commit_free( commit );
1795 git_commit_free( commit );
1796 git_reference_free( branchRef );
1814 if( retval == wxID_ADD )
1816 else if( retval != wxID_OK )
1820 git_reference* branchRef =
nullptr;
1822 if( git_reference_lookup( &branchRef, repo, branchName.mb_str() ) != GIT_OK &&
1823 git_reference_dwim( &branchRef, repo, branchName.mb_str() ) != GIT_OK )
1825 wxString errorMessage = wxString::Format(
_(
"Failed to lookup branch '%s': %s" ),
1826 branchName, giterr_last()->message );
1831 const char* branchRefName = git_reference_name( branchRef );
1833 git_object* branchObj =
nullptr;
1835 if( git_revparse_single( &branchObj, repo, branchName.mb_str() ) != 0 )
1837 wxString errorMessage =
1838 wxString::Format(
_(
"Failed to find branch head for '%s'" ), branchName );
1840 git_reference_free( branchRef );
1846 if( git_checkout_tree( repo, branchObj,
nullptr ) != 0 )
1848 wxString errorMessage =
1849 wxString::Format(
_(
"Failed to switch to branch '%s'" ), branchName );
1851 git_reference_free( branchRef );
1852 git_object_free( branchObj );
1857 if( git_repository_set_head( repo, branchRefName ) != 0 )
1859 wxString errorMessage = wxString::Format(
1860 _(
"Failed to update HEAD reference for branch '%s'" ), branchName );
1862 git_reference_free( branchRef );
1863 git_object_free( branchObj );
1868 git_reference_free( branchRef );
1869 git_object_free( branchObj );
1878 || !
IsOK( wxGetTopLevelParent(
this ),
1879 _(
"Are you sure you want to remove git tracking from this project?" ) ) )
1885 git_repository_free( repo );
1890 fn.AppendDir(
".git" );
1900 std::stack<wxTreeItemId> items;
1903 while( !items.empty() )
1905 wxTreeItemId current = items.top();
1909 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
1911 wxTreeItemIdValue cookie;
1912 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
1914 while( child.IsOk() )
1916 items.push( child );
1933 if( timeSinceLastUpdate.Abs() < wxTimeSpan::Seconds( 2 ) )
1950 git_reference* currentBranchReference =
nullptr;
1951 git_repository_head( ¤tBranchReference, repo );
1954 wxFileName rootFilename( rootItem->
GetFileName() );
1955 wxString repoWorkDir( git_repository_workdir( repo ) );
1958 if( currentBranchReference )
1960 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
1961 wxString branchName = git_reference_shorthand( currentBranchReference );
1963 m_TreeProject->SetItemText( kid, filename +
" [" + branchName +
"]" );
1964 git_reference_free( currentBranchReference );
1969 wxLogError(
"Failed to lookup current branch: %s", giterr_last()->message );
1975 std::map<wxString, wxTreeItemId> branchMap;
1977 std::stack<wxTreeItemId> items;
1980 while( !items.empty() )
1989 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
1991 branchMap[gitAbsPath] = kid;
1993 wxTreeItemIdValue cookie;
1994 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
1996 while( child.IsOk() )
1998 items.push( child );
2004 wxFileName relative = rootFilename;
2005 relative.MakeRelativeTo( repoWorkDir );
2006 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
2009 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
2012 const char* pathspec[] = { pathspecStr.c_str().AsChar() };
2014 git_status_options status_options;
2015 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2016 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2017 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
2018 status_options.pathspec = { (
char**) pathspec, 1 };
2020 git_index* index =
nullptr;
2022 if( git_repository_index( &index, repo ) != GIT_OK )
2025 wxLogTrace(
traceGit, wxS(
"Failed to get git index: %s" ), giterr_last()->message );
2029 git_status_list* status_list =
nullptr;
2031 if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
2033 wxLogTrace(
traceGit, wxS(
"Failed to get git status list: %s" ), giterr_last()->message );
2034 git_index_free( index );
2040 size_t count = git_status_list_entrycount( status_list );
2042 for(
size_t ii = 0; ii < count; ++ii )
2044 const git_status_entry* entry = git_status_byindex( status_list, ii );
2045 std::string
path( entry->head_to_index? entry->head_to_index->old_file.path
2046 : entry->index_to_workdir->old_file.path );
2048 wxString absPath = repoWorkDir;
2051 auto iter = branchMap.find( absPath );
2053 if( iter == branchMap.end() )
2059 if( entry->status == GIT_STATUS_CURRENT )
2065 else if( entry->status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
2072 else if( entry->status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
2079 else if( entry->status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
2088 if( localChanges.count(
path ) )
2095 else if( remoteChanges.count(
path ) )
2111 git_status_list_free( status_list );
2112 git_index_free( index );
2122 if( repo ==
nullptr )
2124 wxMessageBox(
"The selected directory is not a git project." );
2128 git_config*
config =
nullptr;
2129 git_repository_config( &
config, repo );
2132 wxString authorName;
2133 wxString authorEmail;
2136 git_config_entry* name_c =
nullptr;
2137 git_config_entry* email_c =
nullptr;
2138 int authorNameError = git_config_get_entry( &name_c,
config,
"user.name" );
2140 if( authorNameError != 0 || name_c ==
nullptr )
2146 authorName = name_c->value;
2147 git_config_entry_free( name_c );
2151 int authorEmailError = git_config_get_entry( &email_c,
config,
"user.email" );
2153 if( authorEmailError != 0 || email_c ==
nullptr )
2159 authorEmail = email_c->value;
2160 git_config_entry_free( email_c );
2164 git_config_free(
config );
2167 git_status_options status_options;
2168 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2169 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2170 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2172 git_status_list* status_list =
nullptr;
2173 git_status_list_new( &status_list, repo, &status_options );
2175 std::map<wxString, int> modifiedFiles;
2177 size_t count = git_status_list_entrycount( status_list );
2179 std::set<wxString> selected_files;
2183 if( item->GetType() != TREE_FILE_TYPE::DIRECTORY )
2184 selected_files.emplace( item->GetFileName() );
2187 for(
size_t i = 0; i < count; ++i )
2189 const git_status_entry* entry = git_status_byindex( status_list, i );
2192 if( entry->status == GIT_STATUS_CURRENT
2193 || ( entry->status & ( GIT_STATUS_CONFLICTED | GIT_STATUS_IGNORED ) ) )
2198 wxFileName fn( entry->index_to_workdir->old_file.path );
2199 fn.MakeAbsolute( git_repository_workdir( repo ) );
2201 wxString filePath( entry->index_to_workdir->old_file.path, wxConvUTF8 );
2205 modifiedFiles.emplace( filePath, entry->status );
2207 else if( selected_files.count( fn.GetFullPath() ) )
2209 modifiedFiles.emplace( filePath, entry->status );
2213 git_status_list_free( status_list );
2216 DIALOG_GIT_COMMIT dlg( wxGetTopLevelParent(
this ), repo, authorName, authorEmail,
2220 if( ret == wxID_OK )
2224 git_tree* tree =
nullptr;
2225 git_commit* parent =
nullptr;
2226 git_index* index =
nullptr;
2232 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2238 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2242 if( git_repository_index( &index, repo ) != 0 )
2244 wxMessageBox(
_(
"Failed to get repository index: %s" ), giterr_last()->message );
2248 for( wxString& file :files )
2250 if( git_index_add_bypath( index, file.mb_str() ) != 0 )
2252 wxMessageBox(
_(
"Failed to add file to index: %s" ), giterr_last()->message );
2253 git_index_free( index );
2258 if( git_index_write( index ) != 0 )
2260 wxMessageBox(
_(
"Failed to write index: %s" ), giterr_last()->message );
2261 git_index_free( index );
2265 if (git_index_write_tree( &tree_id, index ) != 0)
2267 wxMessageBox(
_(
"Failed to write tree: %s" ), giterr_last()->message );
2268 git_index_free( index );
2272 git_index_free( index );
2274 if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
2276 wxMessageBox(
_(
"Failed to lookup tree: %s" ), giterr_last()->message );
2280 git_reference* headRef =
nullptr;
2282 if( git_repository_head( &headRef, repo ) != 0 )
2284 wxMessageBox(
_(
"Failed to get HEAD reference: %s" ), giterr_last()->message );
2285 git_index_free( index );
2289 if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
2291 wxMessageBox(
_(
"Failed to get commit: %s" ), giterr_last()->message );
2292 git_reference_free( headRef );
2293 git_index_free( index );
2297 git_reference_free( headRef );
2303 git_signature* author =
nullptr;
2305 if( git_signature_now( &author, author_name.mb_str(), author_email.mb_str() ) != 0 )
2307 wxMessageBox(
_(
"Failed to create author signature: %s" ), giterr_last()->message );
2313#if( LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR == 8 \
2314 && ( LIBGIT2_VER_REVISION < 2 || LIBGIT2_VER_REVISION == 3 ) )
2333 git_commit*
const parents[1] = { parent };
2336 const git_commit* parents[1] = { parent };
2339 if( git_commit_create( &oid, repo,
"HEAD", author, author,
nullptr, commit_msg.mb_str(), tree,
2342 wxMessageBox(
_(
"Failed to create commit: %s" ), giterr_last()->message );
2346 git_signature_free( author );
2347 git_commit_free( parent );
2348 git_tree_free( tree );
2369 if( git_repository_index( &index, repo ) != 0 )
2373 if( git_index_find( &entry_pos, index, aFile.mb_str() ) == 0 )
2375 git_index_free( index );
2379 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.