30#include <wx/stdpaths.h>
33#include <wx/textdlg.h>
52#include <wx/dcclient.h>
53#include <wx/settings.h>
99 wxT(
"^.*\\.kicad_pro$" ),
102 wxT(
"^.*\\.kicad_sch$" ),
103 wxT(
"^[^$].*\\.brd$" ),
104 wxT(
"^[^$].*\\.kicad_pcb$" ),
105 wxT(
"^[^$].*\\.kicad_dru$" ),
106 wxT(
"^[^$].*\\.kicad_wks$" ),
107 wxT(
"^[^$].*\\.kicad_mod$" ),
111 wxT(
"^.*\\.kicad_sym$" ),
116 wxT(
"^.*\\.gbrjob$" ),
117 wxT(
"^.*\\.gb[alops]$" ),
118 wxT(
"^.*\\.gt[alops]$" ),
119 wxT(
"^.*\\.g[0-9]{1,2}$" ),
120 wxT(
"^.*\\.gm[0-9]{1,2}$" ),
124 wxT(
"^.*\\.html$" ),
135 wxT(
"^.*\\.kicad_jobset" ),
181 wxSashLayoutWindow( parent,
ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize,
182 wxNO_BORDER | wxTAB_TRAVERSAL )
185 m_TreeProject =
nullptr;
186 m_isRenaming =
false;
187 m_selectedItem =
nullptr;
188 m_watcherNeedReset =
false;
189 m_lastGitStatusUpdate = wxDateTime::Now();
190 m_gitLastError = GIT_ERROR_NONE;
193 Connect( wxEVT_FSWATCHER,
196 Bind( wxEVT_SYS_COLOUR_CHANGED,
206 m_filters.emplace_back( wxT(
"^no KiCad files found" ) );
215 Disconnect( wxEVT_FSWATCHER,
217 Unbind( wxEVT_SYS_COLOUR_CHANGED,
239 if( tree_data.size() != 1 )
242 wxString prj_filename = tree_data[0]->GetFileName();
256 wxString curr_dir = item_data->GetDir();
258 if( curr_dir.IsEmpty() )
264 if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
267 if( !curr_dir.IsEmpty() )
268 curr_dir += wxFileName::GetPathSeparator();
286 wxString curr_dir = item_data->GetDir();
288 if( curr_dir.IsEmpty() )
291 wxString new_dir = wxGetTextFromUser(
_(
"Directory name:" ),
_(
"Create New Directory" ) );
293 if( new_dir.IsEmpty() )
296 wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
298 if( !wxMkdir( full_dirname ) )
328 case TREE_FILE_TYPE::DRILL_NC:
return "nc";
329 case TREE_FILE_TYPE::DRILL_XNC:
return "xnc";
339 case TREE_FILE_TYPE::ROOT:
340 case TREE_FILE_TYPE::UNKNOWN:
341 case TREE_FILE_TYPE::MAX:
342 case TREE_FILE_TYPE::DIRECTORY:
break;
345 return wxEmptyString;
351 std::vector<wxString> projects;
352 wxString dir_filename;
353 bool haveFile = dir.GetFirst( &dir_filename );
357 wxFileName file( dir_filename );
361 projects.push_back( file.GetName() );
363 haveFile = dir.GetNext( &dir_filename );
372 git_repository* repo =
nullptr;
376 if( git_repository_discover( &repo_path, filename, 0, NULL ) )
379 printf(
"get_git_repository_for_file: %s\n", git_error_last()->message ); fflush( 0 );
384 if( git_repository_open( &repo, repo_path.ptr ) )
386 git_buf_free( &repo_path );
391 git_buf_free( &repo_path );
398 const wxTreeItemId& aParent,
399 std::vector<wxString>* aProjectNames,
403 wxFileName fn( aName );
406 return wxTreeItemId();
408 if( wxDirExists( aName ) )
410 type = TREE_FILE_TYPE::DIRECTORY;
416 bool addFile =
false;
418 for(
const wxString& m_filter :
m_filters )
420 wxCHECK2_MSG( reg.Compile( m_filter, wxRE_ICASE ),
continue,
421 wxString::Format(
"Regex %s failed to compile.", m_filter ) );
423 if( reg.Matches( aName ) )
431 return wxTreeItemId();
433 for(
int i =
static_cast<int>( TREE_FILE_TYPE::LEGACY_PROJECT );
434 i < static_cast<int>( TREE_FILE_TYPE::MAX ); i++ )
438 if( ext == wxT(
"" ) )
441 if( reg.Compile( wxString::FromAscii(
"^.*\\." ) + ext + wxString::FromAscii(
"$" ),
442 wxRE_ICASE ) && reg.Matches( aName ) )
450 wxString file = wxFileNameFromPath( aName );
451 wxFileName currfile( file );
455 if( ( type == TREE_FILE_TYPE::LEGACY_PROJECT )
456 && ( currfile.GetName().CmpNoCase(
project.GetName() ) == 0 ) )
458 return wxTreeItemId();
461 if( currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::LEGACY_SCHEMATIC )
462 || currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::SEXPR_SCHEMATIC ) )
467 return wxTreeItemId();
472 wxDir parentDir( parentTreeItem->
GetDir() );
473 std::vector<wxString> projects =
getProjects( parentDir );
476 return wxTreeItemId();
481 wxTreeItemIdValue cookie;
482 wxTreeItemId kid =
m_TreeProject->GetFirstChild( aParent, cookie );
489 return itemData->GetId();
495 if( type == TREE_FILE_TYPE::LEGACY_PROJECT || type == TREE_FILE_TYPE::JSON_PROJECT
496 || type == TREE_FILE_TYPE::LEGACY_SCHEMATIC || type == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
508 if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
512 case TREE_FILE_TYPE::LEGACY_PROJECT:
513 if( itemData->
GetType() == TREE_FILE_TYPE::JSON_PROJECT )
514 return wxTreeItemId();
518 case TREE_FILE_TYPE::LEGACY_SCHEMATIC:
519 if( itemData->
GetType() == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
520 return wxTreeItemId();
524 case TREE_FILE_TYPE::JSON_PROJECT:
525 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_PROJECT )
530 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
531 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_SCHEMATIC )
547 wxTreeItemId newItemId =
m_TreeProject->AppendItem( aParent, file );
554 wxString fileName = currfile.GetName().Lower();
555 wxString projName =
project.GetName().Lower();
557 if( fileName == projName || fileName.StartsWith( projName +
"-" ) )
561 bool subdir_populated =
false;
566 if( TREE_FILE_TYPE::DIRECTORY == type && aRecurse )
572 std::vector<wxString> projects =
getProjects( dir );
573 wxString dir_filename;
574 bool haveFile = dir.GetFirst( &dir_filename );
579 subdir_populated = aRecurse;
585 wxString
path = aName + wxFileName::GetPathSeparator() + dir_filename;
588 haveFile = dir.GetNext( &dir_filename );
597 if( subdir_populated )
623 wxFileName fn = pro_dir;
624 bool prjReset =
false;
635 bool prjOpened = fn.FileExists();
648 if( !prjOpened && !prjReset )
651 prjOpened = fn.FileExists();
658 m_root =
m_TreeProject->AddRoot( fn.GetFullName(),
static_cast<int>( TREE_FILE_TYPE::ROOT ),
659 static_cast<int>( TREE_FILE_TYPE::ROOT ) );
672 wxDir dir( pro_dir );
676 std::vector<wxString> projects =
getProjects( dir );
678 bool haveFile = dir.GetFirst( &filename );
682 if( filename != fn.GetFullName() )
684 wxString
name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
691 haveFile = dir.GetNext( &filename );
715 git_status_options opts;
716 git_status_init_options( &opts, GIT_STATUS_OPTIONS_VERSION );
718 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
719 opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
720 | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
722 git_status_list* status_list =
nullptr;
723 int error = git_status_list_new( &status_list, repo, &opts );
725 if( error != GIT_OK )
728 bool has_changed_files = git_status_list_entrycount( status_list ) > 0;
729 git_status_list_free( status_list );
730 return has_changed_files;
736 wxTreeItemId curr_item = Event.GetItem();
743 bool can_switch_to_project =
true;
744 bool can_create_new_directory =
true;
745 bool can_open_this_directory =
true;
746 bool can_edit =
true;
747 bool can_rename =
true;
748 bool can_delete =
true;
749 bool run_jobs =
false;
753 bool vcs_can_init = !vcs_has_repo;
754 bool vcs_can_remove = vcs_has_repo;
757 bool vcs_can_pull = vcs_can_fetch;
758 bool vcs_can_switch = vcs_has_repo;
762#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 )
763 int major, minor, rev;
764 bool libgit_init = ( git_libgit2_version( &major, &minor, &rev ) == GIT_OK );
767 bool libgit_init =
true;
770 vcs_menu &= libgit_init;
772 if( selection.size() == 0 )
776 if( selection.size() != 1 )
778 can_switch_to_project =
false;
779 can_create_new_directory =
false;
788 can_switch_to_project =
false;
794 can_delete = item->CanDelete();
795 can_rename = item->CanRename();
797 switch( item->GetType() )
799 case TREE_FILE_TYPE::JSON_PROJECT:
800 case TREE_FILE_TYPE::LEGACY_PROJECT:
805 can_switch_to_project =
false;
809 can_create_new_directory =
false;
810 can_open_this_directory =
false;
814 case TREE_FILE_TYPE::DIRECTORY:
815 can_switch_to_project =
false;
819 case TREE_FILE_TYPE::ZIP_ARCHIVE:
820 case TREE_FILE_TYPE::PDF:
822 can_switch_to_project =
false;
823 can_create_new_directory =
false;
824 can_open_this_directory =
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();
1282 if( pathModified.GetFullName().IsEmpty() )
1284 subdir = subdir.BeforeLast(
'/' );
1285 fn = fn.BeforeLast(
'/' );
1288 switch( event.GetChangeType() )
1290 case wxFSW_EVENT_DELETE:
1291 case wxFSW_EVENT_CREATE:
1292 case wxFSW_EVENT_RENAME:
1296 case wxFSW_EVENT_MODIFY:
1299 case wxFSW_EVENT_ACCESS:
1306 if( !root_id.IsOk() )
1309 wxTreeItemIdValue cookie;
1310 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1312 switch( event.GetChangeType() )
1314 case wxFSW_EVENT_CREATE:
1329 case wxFSW_EVENT_DELETE:
1343 case wxFSW_EVENT_RENAME :
1345 const wxFileName& newpath =
event.GetNewPath();
1346 wxString newdir = newpath.GetPath();
1347 wxString newfn = newpath.GetFullPath();
1371 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1377 if( newitem.IsOk() )
1397#if defined( _WIN32 )
1430 fn.AssignDir( prj_dir );
1431 fn.DontFollowLink();
1445 TO_UTF8( fn.GetFullPath() ) );
1453 TO_UTF8( fn.GetFullPath() ) );
1458 if( m_TreeProject->IsEmpty() )
1462 wxTreeItemIdValue cookie;
1463 wxTreeItemId root_id = m_root;
1465 std::stack < wxTreeItemId > subdirs_id;
1467 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1468 int total_watch_count = 0;
1474 if( subdirs_id.empty() )
1480 root_id = subdirs_id.top();
1482 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1498 if( wxFileName::IsDirReadable(
path ) )
1500 fn.AssignDir(
path );
1501 m_watcher->Add( fn );
1502 total_watch_count++;
1505 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1506 subdirs_id.push( kid );
1510 kid = m_TreeProject->GetNextChild( root_id, cookie );
1517#if defined(DEBUG) && 1
1518 wxArrayString paths;
1519 m_watcher->GetWatchedPaths( &paths );
1522 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1556 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1557 wxPaintDC dc(
this );
1559 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1560 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1562 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1563 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1577 wxString dir = tree_data->
GetDir();
1581 wxLogError(
"Failed to initialize git project: project directory is empty." );
1586 git_repository* repo =
nullptr;
1587 int error = git_repository_open(&repo, dir.mb_str());
1592 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1595 _(
"The selected directory is already a git project." ) );
1596 git_repository_free( repo );
1602 error = git_repository_init( &repo, dir.mb_str(), 0 );
1606 git_repository_free( repo );
1612 git_error_last()->message );
1626 dlg.SetTitle(
_(
"Set default remote" ) );
1638 git_remote* remote =
nullptr;
1647 fullURL = dlg.
GetRepoURL().StartsWith(
"https" ) ?
"https://" :
"http://";
1655 fullURL.append( wxS(
":" ) );
1659 fullURL.append( wxS(
"@" ) );
1670 error = git_remote_create_with_fetchspec( &remote, repo,
"origin",
1671 fullURL.ToStdString().c_str(),
1672 "+refs/heads/*:refs/remotes/origin/*" );
1674 if( error != GIT_OK )
1680 git_error_last()->message );
1691 _(
"Fetching Remote" ),
1725 _(
"Fetching Remote" ),
1747 _(
"Fetching Remote" ),
1750 if( handler.
PerformPush() != PushResult::Success )
1763 if(
int error = git_reference_name_to_id( &head_oid, aRepo,
"HEAD" ) != 0 )
1765 wxLogError(
"Failed to lookup HEAD reference" );
1770 git_commit* commit =
nullptr;
1771 if(
int error = git_commit_lookup( &commit, aRepo, &head_oid ) != GIT_OK )
1773 wxLogError(
"Failed to lookup commit" );
1777 git_reference* branchRef =
nullptr;
1779 if( git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ) != 0 )
1781 wxLogError(
"Failed to create branch" );
1782 git_commit_free( commit );
1786 git_commit_free( commit );
1787 git_reference_free( branchRef );
1805 if( retval == wxID_ADD )
1807 else if( retval != wxID_OK )
1811 git_reference* branchRef =
nullptr;
1813 if( git_reference_lookup( &branchRef, repo, branchName.mb_str() ) != GIT_OK &&
1814 git_reference_dwim( &branchRef, repo, branchName.mb_str() ) != GIT_OK )
1816 wxString errorMessage = wxString::Format(
_(
"Failed to lookup branch '%s': %s" ),
1817 branchName, giterr_last()->message );
1822 const char* branchRefName = git_reference_name( branchRef );
1824 git_object* branchObj =
nullptr;
1826 if( git_revparse_single( &branchObj, repo, branchName.mb_str() ) != 0 )
1828 wxString errorMessage =
1829 wxString::Format(
_(
"Failed to find branch head for '%s'" ), branchName );
1831 git_reference_free( branchRef );
1837 if( git_checkout_tree( repo, branchObj,
nullptr ) != 0 )
1839 wxString errorMessage =
1840 wxString::Format(
_(
"Failed to switch to branch '%s'" ), branchName );
1842 git_reference_free( branchRef );
1843 git_object_free( branchObj );
1848 if( git_repository_set_head( repo, branchRefName ) != 0 )
1850 wxString errorMessage = wxString::Format(
1851 _(
"Failed to update HEAD reference for branch '%s'" ), branchName );
1853 git_reference_free( branchRef );
1854 git_object_free( branchObj );
1859 git_reference_free( branchRef );
1860 git_object_free( branchObj );
1869 || !
IsOK( wxGetTopLevelParent(
this ),
1870 _(
"Are you sure you want to remove git tracking from this project?" ) ) )
1876 git_repository_free( repo );
1881 fn.AppendDir(
".git" );
1891 std::stack<wxTreeItemId> items;
1894 while( !items.empty() )
1896 wxTreeItemId current = items.top();
1900 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
1902 wxTreeItemIdValue cookie;
1903 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
1905 while( child.IsOk() )
1907 items.push( child );
1924 if( timeSinceLastUpdate.Abs() < wxTimeSpan::Seconds( 2 ) )
1941 git_reference* currentBranchReference =
nullptr;
1942 git_repository_head( ¤tBranchReference, repo );
1945 wxFileName rootFilename( rootItem->
GetFileName() );
1946 wxString repoWorkDir( git_repository_workdir( repo ) );
1949 if( currentBranchReference )
1951 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
1952 wxString branchName = git_reference_shorthand( currentBranchReference );
1954 m_TreeProject->SetItemText( kid, filename +
" [" + branchName +
"]" );
1955 git_reference_free( currentBranchReference );
1960 wxLogError(
"Failed to lookup current branch: %s", giterr_last()->message );
1966 std::map<wxString, wxTreeItemId> branchMap;
1968 std::stack<wxTreeItemId> items;
1971 while( !items.empty() )
1980 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
1982 branchMap[gitAbsPath] = kid;
1984 wxTreeItemIdValue cookie;
1985 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
1987 while( child.IsOk() )
1989 items.push( child );
1995 wxFileName relative = rootFilename;
1996 relative.MakeRelativeTo( repoWorkDir );
1997 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
2000 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
2003 const char* pathspec[] = { pathspecStr.c_str().AsChar() };
2005 git_status_options status_options;
2006 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2007 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2008 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
2009 status_options.pathspec = { (
char**) pathspec, 1 };
2011 git_index* index =
nullptr;
2013 if( git_repository_index( &index, repo ) != GIT_OK )
2016 wxLogTrace(
traceGit, wxS(
"Failed to get git index: %s" ), giterr_last()->message );
2020 git_status_list* status_list =
nullptr;
2022 if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
2024 wxLogTrace(
traceGit, wxS(
"Failed to get git status list: %s" ), giterr_last()->message );
2025 git_index_free( index );
2031 size_t count = git_status_list_entrycount( status_list );
2033 for(
size_t ii = 0; ii < count; ++ii )
2035 const git_status_entry* entry = git_status_byindex( status_list, ii );
2036 std::string
path( entry->head_to_index? entry->head_to_index->old_file.path
2037 : entry->index_to_workdir->old_file.path );
2039 wxString absPath = repoWorkDir;
2042 auto iter = branchMap.find( absPath );
2044 if( iter == branchMap.end() )
2050 if( entry->status == GIT_STATUS_CURRENT )
2056 else if( entry->status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
2063 else if( entry->status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
2070 else if( entry->status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
2079 if( localChanges.count(
path ) )
2086 else if( remoteChanges.count(
path ) )
2102 git_status_list_free( status_list );
2103 git_index_free( index );
2113 if( repo ==
nullptr )
2115 wxMessageBox(
"The selected directory is not a git project." );
2119 git_config*
config =
nullptr;
2120 git_repository_config( &
config, repo );
2123 wxString authorName;
2124 wxString authorEmail;
2127 git_config_entry* name_c =
nullptr;
2128 git_config_entry* email_c =
nullptr;
2129 int authorNameError = git_config_get_entry( &name_c,
config,
"user.name" );
2131 if( authorNameError != 0 || name_c ==
nullptr )
2137 authorName = name_c->value;
2138 git_config_entry_free( name_c );
2142 int authorEmailError = git_config_get_entry( &email_c,
config,
"user.email" );
2144 if( authorEmailError != 0 || email_c ==
nullptr )
2150 authorEmail = email_c->value;
2151 git_config_entry_free( email_c );
2155 git_config_free(
config );
2158 git_status_options status_options;
2159 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2160 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2161 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2163 git_status_list* status_list =
nullptr;
2164 git_status_list_new( &status_list, repo, &status_options );
2166 std::map<wxString, int> modifiedFiles;
2168 size_t count = git_status_list_entrycount( status_list );
2170 std::set<wxString> selected_files;
2174 if( item->GetType() != TREE_FILE_TYPE::DIRECTORY )
2175 selected_files.emplace( item->GetFileName() );
2178 for(
size_t i = 0; i < count; ++i )
2180 const git_status_entry* entry = git_status_byindex( status_list, i );
2183 if( entry->status == GIT_STATUS_CURRENT
2184 || ( entry->status & ( GIT_STATUS_CONFLICTED | GIT_STATUS_IGNORED ) ) )
2189 wxFileName fn( entry->index_to_workdir->old_file.path );
2190 fn.MakeAbsolute( git_repository_workdir( repo ) );
2192 wxString filePath( entry->index_to_workdir->old_file.path, wxConvUTF8 );
2196 modifiedFiles.emplace( filePath, entry->status );
2198 else if( selected_files.count( fn.GetFullPath() ) )
2200 modifiedFiles.emplace( filePath, entry->status );
2204 git_status_list_free( status_list );
2207 DIALOG_GIT_COMMIT dlg( wxGetTopLevelParent(
this ), repo, authorName, authorEmail,
2211 if( ret == wxID_OK )
2215 git_tree* tree =
nullptr;
2216 git_commit* parent =
nullptr;
2217 git_index* index =
nullptr;
2223 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2229 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2233 if( git_repository_index( &index, repo ) != 0 )
2235 wxMessageBox(
_(
"Failed to get repository index: %s" ), giterr_last()->message );
2239 for( wxString& file :files )
2241 if( git_index_add_bypath( index, file.mb_str() ) != 0 )
2243 wxMessageBox(
_(
"Failed to add file to index: %s" ), giterr_last()->message );
2244 git_index_free( index );
2249 if( git_index_write( index ) != 0 )
2251 wxMessageBox(
_(
"Failed to write index: %s" ), giterr_last()->message );
2252 git_index_free( index );
2256 if (git_index_write_tree( &tree_id, index ) != 0)
2258 wxMessageBox(
_(
"Failed to write tree: %s" ), giterr_last()->message );
2259 git_index_free( index );
2263 git_index_free( index );
2265 if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
2267 wxMessageBox(
_(
"Failed to lookup tree: %s" ), giterr_last()->message );
2271 git_reference* headRef =
nullptr;
2273 if( git_repository_head( &headRef, repo ) != 0 )
2275 wxMessageBox(
_(
"Failed to get HEAD reference: %s" ), giterr_last()->message );
2276 git_index_free( index );
2280 if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
2282 wxMessageBox(
_(
"Failed to get commit: %s" ), giterr_last()->message );
2283 git_reference_free( headRef );
2284 git_index_free( index );
2288 git_reference_free( headRef );
2294 git_signature* author =
nullptr;
2296 if( git_signature_now( &author, author_name.mb_str(), author_email.mb_str() ) != 0 )
2298 wxMessageBox(
_(
"Failed to create author signature: %s" ), giterr_last()->message );
2304#if( LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR == 8 \
2305 && ( LIBGIT2_VER_REVISION < 2 || LIBGIT2_VER_REVISION == 3 ) )
2324 git_commit*
const parents[1] = { parent };
2327 const git_commit* parents[1] = { parent };
2330 if( git_commit_create( &oid, repo,
"HEAD", author, author,
nullptr, commit_msg.mb_str(), tree,
2333 wxMessageBox(
_(
"Failed to create commit: %s" ), giterr_last()->message );
2337 git_signature_free( author );
2338 git_commit_free( parent );
2339 git_tree_free( tree );
2360 if( git_repository_index( &index, repo ) != 0 )
2364 if( git_index_find( &entry_pos, index, aFile.mb_str() ) == 0 )
2366 git_index_free( index );
2370 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)
void SetSSHKey(const wxString &aSSHKey)
void SetUsername(const wxString &aUsername)
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
void UpdateCurrentBranchInfo()
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.
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_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)
Remove 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.