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$" ),
178 wxSashLayoutWindow( parent,
ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize,
179 wxNO_BORDER | wxTAB_TRAVERSAL )
182 m_TreeProject =
nullptr;
183 m_isRenaming =
false;
184 m_selectedItem =
nullptr;
185 m_watcherNeedReset =
false;
186 m_lastGitStatusUpdate = wxDateTime::Now();
187 m_gitLastError = GIT_ERROR_NONE;
190 Connect( wxEVT_FSWATCHER,
193 Bind( wxEVT_SYS_COLOUR_CHANGED,
203 m_filters.emplace_back( wxT(
"^no KiCad files found" ) );
232 if( tree_data.size() != 1 )
235 wxString prj_filename = tree_data[0]->GetFileName();
249 wxString curr_dir = item_data->GetDir();
251 if( curr_dir.IsEmpty() )
257 if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
260 if( !curr_dir.IsEmpty() )
261 curr_dir += wxFileName::GetPathSeparator();
279 wxString curr_dir = item_data->GetDir();
281 if( curr_dir.IsEmpty() )
284 wxString new_dir = wxGetTextFromUser(
_(
"Directory name:" ),
_(
"Create New Directory" ) );
286 if( new_dir.IsEmpty() )
289 wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
291 if( !wxMkdir( full_dirname ) )
321 case TREE_FILE_TYPE::DRILL_NC:
return "nc";
322 case TREE_FILE_TYPE::DRILL_XNC:
return "xnc";
331 case TREE_FILE_TYPE::ROOT:
332 case TREE_FILE_TYPE::UNKNOWN:
333 case TREE_FILE_TYPE::MAX:
334 case TREE_FILE_TYPE::DIRECTORY:
break;
337 return wxEmptyString;
343 std::vector<wxString> projects;
344 wxString dir_filename;
345 bool haveFile = dir.GetFirst( &dir_filename );
349 wxFileName file( dir_filename );
353 projects.push_back( file.GetName() );
355 haveFile = dir.GetNext( &dir_filename );
364 git_repository* repo =
nullptr;
368 if( git_repository_discover( &repo_path, filename, 0, NULL ) )
371 printf(
"get_git_repository_for_file: %s\n", git_error_last()->message ); fflush( 0 );
376 if( git_repository_open( &repo, repo_path.ptr ) )
378 git_buf_free( &repo_path );
383 git_buf_free( &repo_path );
390 const wxTreeItemId& aParent,
391 std::vector<wxString>* aProjectNames,
395 wxFileName fn( aName );
398 return wxTreeItemId();
400 if( wxDirExists( aName ) )
402 type = TREE_FILE_TYPE::DIRECTORY;
408 bool addFile =
false;
410 for(
const wxString& m_filter :
m_filters )
412 wxCHECK2_MSG( reg.Compile( m_filter, wxRE_ICASE ),
continue,
413 wxString::Format(
"Regex %s failed to compile.", m_filter ) );
415 if( reg.Matches( aName ) )
423 return wxTreeItemId();
425 for(
int i =
static_cast<int>( TREE_FILE_TYPE::LEGACY_PROJECT );
426 i < static_cast<int>( TREE_FILE_TYPE::MAX ); i++ )
430 if( ext == wxT(
"" ) )
433 if( reg.Compile( wxString::FromAscii(
"^.*\\." ) + ext + wxString::FromAscii(
"$" ),
434 wxRE_ICASE ) && reg.Matches( aName ) )
442 wxString file = wxFileNameFromPath( aName );
443 wxFileName currfile( file );
447 if( ( type == TREE_FILE_TYPE::LEGACY_PROJECT )
448 && ( currfile.GetName().CmpNoCase(
project.GetName() ) == 0 ) )
450 return wxTreeItemId();
453 if( currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::LEGACY_SCHEMATIC )
454 || currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::SEXPR_SCHEMATIC ) )
459 return wxTreeItemId();
464 wxDir parentDir( parentTreeItem->
GetDir() );
465 std::vector<wxString> projects =
getProjects( parentDir );
468 return wxTreeItemId();
473 wxTreeItemIdValue cookie;
474 wxTreeItemId kid =
m_TreeProject->GetFirstChild( aParent, cookie );
481 return itemData->GetId();
487 if( type == TREE_FILE_TYPE::LEGACY_PROJECT || type == TREE_FILE_TYPE::JSON_PROJECT
488 || type == TREE_FILE_TYPE::LEGACY_SCHEMATIC || type == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
500 if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
504 case TREE_FILE_TYPE::LEGACY_PROJECT:
505 if( itemData->
GetType() == TREE_FILE_TYPE::JSON_PROJECT )
506 return wxTreeItemId();
510 case TREE_FILE_TYPE::LEGACY_SCHEMATIC:
511 if( itemData->
GetType() == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
512 return wxTreeItemId();
516 case TREE_FILE_TYPE::JSON_PROJECT:
517 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_PROJECT )
522 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
523 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_SCHEMATIC )
539 wxTreeItemId newItemId =
m_TreeProject->AppendItem( aParent, file );
546 wxString fileName = currfile.GetName().Lower();
547 wxString projName =
project.GetName().Lower();
549 if( fileName == projName || fileName.StartsWith( projName +
"-" ) )
553 bool subdir_populated =
false;
558 if( TREE_FILE_TYPE::DIRECTORY == type && aRecurse )
564 std::vector<wxString> projects =
getProjects( dir );
565 wxString dir_filename;
566 bool haveFile = dir.GetFirst( &dir_filename );
571 subdir_populated = aRecurse;
577 wxString
path = aName + wxFileName::GetPathSeparator() + dir_filename;
580 haveFile = dir.GetNext( &dir_filename );
589 if( subdir_populated )
615 wxFileName fn = pro_dir;
616 bool prjReset =
false;
627 bool prjOpened = fn.FileExists();
639 if( conn_type ==
"https" )
641 else if( conn_type ==
"ssh" )
649 if( !prjOpened && !prjReset )
652 prjOpened = fn.FileExists();
659 m_root =
m_TreeProject->AddRoot( fn.GetFullName(),
static_cast<int>( TREE_FILE_TYPE::ROOT ),
660 static_cast<int>( TREE_FILE_TYPE::ROOT ) );
673 wxDir dir( pro_dir );
677 std::vector<wxString> projects =
getProjects( dir );
679 bool haveFile = dir.GetFirst( &filename );
683 if( filename != fn.GetFullName() )
685 wxString
name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
692 haveFile = dir.GetNext( &filename );
716 git_status_options opts;
717 git_status_init_options( &opts, GIT_STATUS_OPTIONS_VERSION );
719 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
720 opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
721 | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
723 git_status_list* status_list =
nullptr;
724 int error = git_status_list_new( &status_list, repo, &opts );
726 if( error != GIT_OK )
729 bool has_changed_files = git_status_list_entrycount( status_list ) > 0;
730 git_status_list_free( status_list );
731 return has_changed_files;
737 wxTreeItemId curr_item = Event.GetItem();
744 bool can_switch_to_project =
true;
745 bool can_create_new_directory =
true;
746 bool can_open_this_directory =
true;
747 bool can_edit =
true;
748 bool can_rename =
true;
749 bool can_delete =
true;
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::SEXPR_SCHEMATIC:
820 case TREE_FILE_TYPE::SEXPR_PCB:
824 can_switch_to_project =
false;
825 can_create_new_directory =
false;
826 can_open_this_directory =
false;
836 if( can_switch_to_project )
839 _(
"Close all editors, and switch to the selected project" ),
840 KiBitmap( BITMAPS::open_project ) );
841 popup_menu.AppendSeparator();
844 if( can_create_new_directory )
847 _(
"Create a New Directory" ),
KiBitmap( BITMAPS::directory ) );
850 if( can_open_this_directory )
852 if( selection.size() == 1 )
855 text =
_(
"Reveal in Finder" );
856 help_text =
_(
"Reveals the directory in a Finder window" );
858 text =
_(
"Open Directory in File Explorer" );
859 help_text =
_(
"Opens the directory in the default system file manager" );
865 text =
_(
"Reveal in Finder" );
866 help_text =
_(
"Reveals the directories in a Finder window" );
868 text =
_(
"Open Directories in File Explorer" );
869 help_text =
_(
"Opens the directories in the default system file manager" );
874 KiBitmap( BITMAPS::directory_browser ) );
879 if( selection.size() == 1 )
880 help_text =
_(
"Open the file in a Text Editor" );
882 help_text =
_(
"Open files in a Text Editor" );
890 if( selection.size() == 1 )
892 text =
_(
"Rename File..." );
893 help_text =
_(
"Rename file" );
897 text =
_(
"Rename Files..." );
898 help_text =
_(
"Rename files" );
907 if( selection.size() == 1 )
908 help_text =
_(
"Delete the file and its content" );
910 help_text =
_(
"Delete the files and their contents" );
912 if( can_switch_to_project
913 || can_create_new_directory
914 || can_open_this_directory
918 popup_menu.AppendSeparator();
932 wxMenu* vcs_submenu =
new wxMenu();
933 wxMenu* branch_submenu =
new wxMenu();
934 wxMenuItem* vcs_menuitem =
nullptr;
937 _(
"Add Project to Version Control..." ),
938 _(
"Initialize a new repository" ) );
939 vcs_menuitem->Enable( vcs_can_init );
943 _(
"Commit changes to the local repository" ) );
944 vcs_menuitem->Enable( vcs_can_commit );
946 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PUSH,
_(
"Push" ),
947 _(
"Push committed local changes to remote repository" ) );
948 vcs_menuitem->Enable( vcs_can_push );
950 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PULL,
_(
"Pull" ),
951 _(
"Pull changes from remote repository into local" ) );
952 vcs_menuitem->Enable( vcs_can_pull );
954 vcs_submenu->AppendSeparator();
957 _(
"Commit changes to the local repository" ) );
958 vcs_menuitem->Enable( vcs_can_commit );
960 vcs_submenu->AppendSeparator();
969 for(
size_t ii = 1; ii < branchNames.size() && ii < 6; ++ii )
971 wxString msg =
_(
"Switch to branch " ) + branchNames[ii];
973 vcs_menuitem->Enable( vcs_can_switch );
977 _(
"Switch to a different branch" ) );
978 vcs_menuitem->Enable( vcs_can_switch );
982 vcs_submenu->AppendSeparator();
985 _(
"Delete all version control files from the project directory." ) );
986 vcs_menuitem->Enable( vcs_can_remove );
988 popup_menu.AppendSeparator();
989 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
992 if( popup_menu.GetMenuItemCount() > 0 )
993 PopupMenu( &popup_menu );
1001 if( editorname.IsEmpty() )
1003 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1011 wxString fullFileName = item_data->GetFileName();
1013 if( !fullFileName.IsEmpty() )
1015 ExecuteFile( editorname, fullFileName.wc_str(),
nullptr,
false );
1026 item_data->Delete();
1036 if( tree_data.size() != 1 )
1040 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1041 tree_data[0]->GetFileName() );
1042 wxTextEntryDialog dlg( wxGetTopLevelParent(
this ), msg,
_(
"Change filename" ), buffer );
1044 if( dlg.ShowModal() != wxID_OK )
1047 buffer = dlg.GetValue();
1048 buffer.Trim(
true );
1049 buffer.Trim(
false );
1051 if( buffer.IsEmpty() )
1054 tree_data[0]->Rename( buffer,
true );
1063 if( tree_data.size() != 1 )
1100 wxTreeItemId itemId = Event.GetItem();
1106 if( tree_data->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1110 wxTreeItemIdValue cookie;
1111 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1114 bool subdir_populated =
false;
1117 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1121 if( !itemData || itemData->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1128 wxDir dir( fileName );
1130 if( dir.IsOpened() )
1132 std::vector<wxString> projects =
getProjects( dir );
1133 wxString dir_filename;
1134 bool haveFile = dir.GetFirst( &dir_filename );
1139 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1142 haveFile = dir.GetNext( &dir_filename );
1148 subdir_populated =
true;
1157 if( subdir_populated )
1165 wxArrayTreeItemIds selection;
1166 std::vector<PROJECT_TREE_ITEM*> data;
1170 for(
auto it = selection.begin(); it != selection.end(); it++ )
1176 wxLogTrace(
traceGit, wxS(
"Null tree item returned for selection, dynamic_cast "
1181 data.push_back( item );
1200 if( prj_dir == aSubDir )
1204 wxTreeItemIdValue cookie;
1205 wxTreeItemId root_id =
m_root;
1206 std::stack<wxTreeItemId> subdirs_id;
1208 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1212 if( ! child.IsOk() )
1214 if( subdirs_id.empty() )
1221 root_id = subdirs_id.top();
1232 if( itemData && ( itemData->
GetType() == TREE_FILE_TYPE::DIRECTORY ) )
1242 subdirs_id.push( child );
1258 const wxFileName& pathModified =
event.GetPath();
1259 wxString subdir = pathModified.GetPath();
1260 wxString fn = pathModified.GetFullPath();
1262 switch( event.GetChangeType() )
1264 case wxFSW_EVENT_DELETE:
1265 case wxFSW_EVENT_CREATE:
1266 case wxFSW_EVENT_RENAME:
1270 case wxFSW_EVENT_MODIFY:
1273 case wxFSW_EVENT_ACCESS:
1280 if( !root_id.IsOk() )
1283 wxTreeItemIdValue cookie;
1284 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1286 switch( event.GetChangeType() )
1288 case wxFSW_EVENT_CREATE:
1290 wxTreeItemId newitem =
1304 case wxFSW_EVENT_DELETE:
1318 case wxFSW_EVENT_RENAME :
1320 const wxFileName& newpath =
event.GetNewPath();
1321 wxString newdir = newpath.GetPath();
1322 wxString newfn = newpath.GetFullPath();
1346 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1352 if( newitem.IsOk() )
1372#if defined( _WIN32 )
1405 fn.AssignDir( prj_dir );
1406 fn.DontFollowLink();
1420 TO_UTF8( fn.GetFullPath() ) );
1428 TO_UTF8( fn.GetFullPath() ) );
1433 if( m_TreeProject->IsEmpty() )
1437 wxTreeItemIdValue cookie;
1438 wxTreeItemId root_id = m_root;
1440 std::stack < wxTreeItemId > subdirs_id;
1442 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1443 int total_watch_count = 0;
1449 if( subdirs_id.empty() )
1455 root_id = subdirs_id.top();
1457 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1473 if( wxFileName::IsDirReadable(
path ) )
1475 fn.AssignDir(
path );
1476 m_watcher->Add( fn );
1477 total_watch_count++;
1480 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1481 subdirs_id.push( kid );
1485 kid = m_TreeProject->GetNextChild( root_id, cookie );
1492#if defined(DEBUG) && 1
1493 wxArrayString paths;
1494 m_watcher->GetWatchedPaths( &paths );
1497 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1531 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1532 wxPaintDC dc(
this );
1534 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1535 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1537 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1538 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1552 wxString dir = tree_data->
GetDir();
1556 wxLogError(
"Failed to initialize git project: project directory is empty." );
1561 git_repository* repo =
nullptr;
1562 int error = git_repository_open(&repo, dir.mb_str());
1567 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1570 _(
"The selected directory is already a git project." ) );
1571 git_repository_free( repo );
1577 error = git_repository_init( &repo, dir.mb_str(), 0 );
1581 git_repository_free( repo );
1587 git_error_last()->message );
1601 dlg.SetTitle(
_(
"Set default remote" ) );
1603 if( dlg.ShowModal() != wxID_OK )
1613 git_remote* remote =
nullptr;
1622 fullURL = dlg.
GetRepoURL().StartsWith(
"https" ) ?
"https://" :
"http://";
1630 fullURL.append( wxS(
":" ) );
1634 fullURL.append( wxS(
"@" ) );
1645 error = git_remote_create_with_fetchspec( &remote, repo,
"origin",
1646 fullURL.ToStdString().c_str(),
1647 "+refs/heads/*:refs/remotes/origin/*" );
1649 if( error != GIT_OK )
1655 git_error_last()->message );
1671 _(
"Fetching Remote" ),
1710 _(
"Fetching Remote" ),
1732 _(
"Fetching Remote" ),
1735 if( handler.
PerformPush() != PushResult::Success )
1748 if(
int error = git_reference_name_to_id( &head_oid, aRepo,
"HEAD" ) != 0 )
1750 wxLogError(
"Failed to lookup HEAD reference" );
1755 git_commit* commit =
nullptr;
1756 if(
int error = git_commit_lookup( &commit, aRepo, &head_oid ) != GIT_OK )
1758 wxLogError(
"Failed to lookup commit" );
1762 git_reference* branchRef =
nullptr;
1764 if( git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ) != 0 )
1766 wxLogError(
"Failed to create branch" );
1767 git_commit_free( commit );
1771 git_commit_free( commit );
1772 git_reference_free( branchRef );
1787 int retval = dlg.ShowModal();
1790 if( retval == wxID_ADD )
1792 else if( retval != wxID_OK )
1796 git_reference* branchRef =
nullptr;
1798 if( git_reference_lookup( &branchRef, repo, branchName.mb_str() ) != GIT_OK &&
1799 git_reference_dwim( &branchRef, repo, branchName.mb_str() ) != GIT_OK )
1801 wxString errorMessage = wxString::Format(
_(
"Failed to lookup branch '%s': %s" ),
1802 branchName, giterr_last()->message );
1807 const char* branchRefName = git_reference_name( branchRef );
1809 git_object* branchObj =
nullptr;
1811 if( git_revparse_single( &branchObj, repo, branchName.mb_str() ) != 0 )
1813 wxString errorMessage =
1814 wxString::Format(
_(
"Failed to find branch head for '%s'" ), branchName );
1816 git_reference_free( branchRef );
1822 if( git_checkout_tree( repo, branchObj,
nullptr ) != 0 )
1824 wxString errorMessage =
1825 wxString::Format(
_(
"Failed to switch to branch '%s'" ), branchName );
1827 git_reference_free( branchRef );
1828 git_object_free( branchObj );
1833 if( git_repository_set_head( repo, branchRefName ) != 0 )
1835 wxString errorMessage = wxString::Format(
1836 _(
"Failed to update HEAD reference for branch '%s'" ), branchName );
1838 git_reference_free( branchRef );
1839 git_object_free( branchObj );
1844 git_reference_free( branchRef );
1845 git_object_free( branchObj );
1854 || !
IsOK( wxGetTopLevelParent(
this ),
1855 _(
"Are you sure you want to remove git tracking from this project?" ) ) )
1861 git_repository_free( repo );
1866 fn.AppendDir(
".git" );
1876 std::stack<wxTreeItemId> items;
1879 while( !items.empty() )
1881 wxTreeItemId current = items.top();
1885 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
1887 wxTreeItemIdValue cookie;
1888 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
1890 while( child.IsOk() )
1892 items.push( child );
1909 if( timeSinceLastUpdate.Abs() < wxTimeSpan::Seconds( 2 ) )
1926 git_reference* currentBranchReference =
nullptr;
1927 git_repository_head( ¤tBranchReference, repo );
1930 wxFileName rootFilename( rootItem->
GetFileName() );
1931 wxString repoWorkDir( git_repository_workdir( repo ) );
1934 if( currentBranchReference )
1936 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
1937 wxString branchName = git_reference_shorthand( currentBranchReference );
1939 m_TreeProject->SetItemText( kid, filename +
" [" + branchName +
"]" );
1940 git_reference_free( currentBranchReference );
1945 wxLogError(
"Failed to lookup current branch: %s", giterr_last()->message );
1951 std::map<wxString, wxTreeItemId> branchMap;
1953 std::stack<wxTreeItemId> items;
1956 while( !items.empty() )
1965 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
1967 branchMap[gitAbsPath] = kid;
1969 wxTreeItemIdValue cookie;
1970 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
1972 while( child.IsOk() )
1974 items.push( child );
1980 wxFileName relative = rootFilename;
1981 relative.MakeRelativeTo( repoWorkDir );
1982 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
1985 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
1988 const char* pathspec[] = { pathspecStr.c_str().AsChar() };
1990 git_status_options status_options;
1991 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
1992 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
1993 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
1994 status_options.pathspec = { (
char**) pathspec, 1 };
1996 git_index* index =
nullptr;
1998 if( git_repository_index( &index, repo ) != GIT_OK )
2001 wxLogTrace(
traceGit, wxS(
"Failed to get git index: %s" ), giterr_last()->message );
2005 git_status_list* status_list =
nullptr;
2007 if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
2009 wxLogTrace(
traceGit, wxS(
"Failed to get git status list: %s" ), giterr_last()->message );
2010 git_index_free( index );
2016 size_t count = git_status_list_entrycount( status_list );
2018 for(
size_t ii = 0; ii < count; ++ii )
2020 const git_status_entry* entry = git_status_byindex( status_list, ii );
2021 std::string
path( entry->head_to_index? entry->head_to_index->old_file.path
2022 : entry->index_to_workdir->old_file.path );
2024 wxString absPath = repoWorkDir;
2027 auto iter = branchMap.find( absPath );
2029 if( iter == branchMap.end() )
2035 if( entry->status == GIT_STATUS_CURRENT )
2041 else if( entry->status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
2048 else if( entry->status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
2055 else if( entry->status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
2064 if( localChanges.count(
path ) )
2071 else if( remoteChanges.count(
path ) )
2087 git_status_list_free( status_list );
2088 git_index_free( index );
2098 if( repo ==
nullptr )
2100 wxMessageBox(
"The selected directory is not a git project." );
2104 git_config*
config =
nullptr;
2105 git_repository_config( &
config, repo );
2108 wxString authorName;
2109 wxString authorEmail;
2112 git_config_entry* name_c =
nullptr;
2113 git_config_entry* email_c =
nullptr;
2114 int authorNameError = git_config_get_entry( &name_c,
config,
"user.name" );
2116 if( authorNameError != 0 || name_c ==
nullptr )
2122 authorName = name_c->value;
2123 git_config_entry_free( name_c );
2127 int authorEmailError = git_config_get_entry( &email_c,
config,
"user.email" );
2129 if( authorEmailError != 0 || email_c ==
nullptr )
2135 authorEmail = email_c->value;
2136 git_config_entry_free( email_c );
2140 git_config_free(
config );
2143 git_status_options status_options;
2144 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2145 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2146 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2148 git_status_list* status_list =
nullptr;
2149 git_status_list_new( &status_list, repo, &status_options );
2151 std::map<wxString, int> modifiedFiles;
2153 size_t count = git_status_list_entrycount( status_list );
2155 std::set<wxString> selected_files;
2159 if( item->GetType() != TREE_FILE_TYPE::DIRECTORY )
2160 selected_files.emplace( item->GetFileName() );
2163 for(
size_t i = 0; i < count; ++i )
2165 const git_status_entry* entry = git_status_byindex( status_list, i );
2168 if( entry->status == GIT_STATUS_CURRENT
2169 || ( entry->status & ( GIT_STATUS_CONFLICTED | GIT_STATUS_IGNORED ) ) )
2174 wxFileName fn( entry->index_to_workdir->old_file.path );
2175 fn.MakeAbsolute( git_repository_workdir( repo ) );
2177 wxString filePath( entry->index_to_workdir->old_file.path, wxConvUTF8 );
2181 modifiedFiles.emplace( filePath, entry->status );
2183 else if( selected_files.count( fn.GetFullPath() ) )
2185 modifiedFiles.emplace( filePath, entry->status );
2189 git_status_list_free( status_list );
2192 DIALOG_GIT_COMMIT dlg( wxGetTopLevelParent(
this ), repo, authorName, authorEmail,
2194 auto ret = dlg.ShowModal();
2196 if( ret == wxID_OK )
2200 git_tree* tree =
nullptr;
2201 git_commit* parent =
nullptr;
2202 git_index* index =
nullptr;
2208 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2214 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2218 if( git_repository_index( &index, repo ) != 0 )
2220 wxMessageBox(
_(
"Failed to get repository index: %s" ), giterr_last()->message );
2224 for( wxString& file :files )
2226 if( git_index_add_bypath( index, file.mb_str() ) != 0 )
2228 wxMessageBox(
_(
"Failed to add file to index: %s" ), giterr_last()->message );
2229 git_index_free( index );
2234 if( git_index_write( index ) != 0 )
2236 wxMessageBox(
_(
"Failed to write index: %s" ), giterr_last()->message );
2237 git_index_free( index );
2241 if (git_index_write_tree( &tree_id, index ) != 0)
2243 wxMessageBox(
_(
"Failed to write tree: %s" ), giterr_last()->message );
2244 git_index_free( index );
2248 git_index_free( index );
2250 if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
2252 wxMessageBox(
_(
"Failed to lookup tree: %s" ), giterr_last()->message );
2256 git_reference* headRef =
nullptr;
2258 if( git_repository_head( &headRef, repo ) != 0 )
2260 wxMessageBox(
_(
"Failed to get HEAD reference: %s" ), giterr_last()->message );
2261 git_index_free( index );
2265 if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
2267 wxMessageBox(
_(
"Failed to get commit: %s" ), giterr_last()->message );
2268 git_reference_free( headRef );
2269 git_index_free( index );
2273 git_reference_free( headRef );
2279 git_signature* author =
nullptr;
2281 if( git_signature_now( &author, author_name.mb_str(), author_email.mb_str() ) != 0 )
2283 wxMessageBox(
_(
"Failed to create author signature: %s" ), giterr_last()->message );
2289#if( LIBGIT2_VER_MAJOR > 1 ) || ( LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR >= 8 )
2291 git_commit*
const parents[1] = { parent };
2294 const git_commit* parents[1] = { parent };
2297 if( git_commit_create( &oid, repo,
"HEAD", author, author,
nullptr, commit_msg.mb_str(), tree,
2300 wxMessageBox(
_(
"Failed to create commit: %s" ), giterr_last()->message );
2304 git_signature_free( author );
2305 git_commit_free( parent );
2306 git_tree_free( tree );
2327 if( git_repository_index( &index, repo ) != 0 )
2331 if( git_index_find( &entry_pos, index, aFile.mb_str() ) == 0 )
2333 git_index_free( index );
2337 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 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 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.