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" ),
182 wxSashLayoutWindow( parent,
ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize,
183 wxNO_BORDER | wxTAB_TRAVERSAL )
186 m_TreeProject =
nullptr;
187 m_isRenaming =
false;
188 m_selectedItem =
nullptr;
189 m_watcherNeedReset =
false;
190 m_lastGitStatusUpdate = wxDateTime::Now();
191 m_gitLastError = GIT_ERROR_NONE;
194 Connect( wxEVT_FSWATCHER,
197 Bind( wxEVT_SYS_COLOUR_CHANGED,
207 m_filters.emplace_back( wxT(
"^no KiCad files found" ) );
216 Disconnect( wxEVT_FSWATCHER,
218 Unbind( wxEVT_SYS_COLOUR_CHANGED,
240 if( tree_data.size() != 1 )
243 wxString prj_filename = tree_data[0]->GetFileName();
257 wxString curr_dir = item_data->GetDir();
259 if( curr_dir.IsEmpty() )
265 if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
268 if( !curr_dir.IsEmpty() )
269 curr_dir += wxFileName::GetPathSeparator();
287 wxString curr_dir = item_data->GetDir();
289 if( curr_dir.IsEmpty() )
292 wxString new_dir = wxGetTextFromUser(
_(
"Directory name:" ),
_(
"Create New Directory" ) );
294 if( new_dir.IsEmpty() )
297 wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
299 if( !wxMkdir( full_dirname ) )
329 case TREE_FILE_TYPE::DRILL_NC:
return "nc";
330 case TREE_FILE_TYPE::DRILL_XNC:
return "xnc";
340 case TREE_FILE_TYPE::ROOT:
341 case TREE_FILE_TYPE::UNKNOWN:
342 case TREE_FILE_TYPE::MAX:
343 case TREE_FILE_TYPE::DIRECTORY:
break;
346 return wxEmptyString;
352 std::vector<wxString> projects;
353 wxString dir_filename;
354 bool haveFile = dir.GetFirst( &dir_filename );
358 wxFileName file( dir_filename );
362 projects.push_back( file.GetName() );
364 haveFile = dir.GetNext( &dir_filename );
373 git_repository* repo =
nullptr;
377 if( git_repository_discover( &repo_path, filename, 0, NULL ) )
380 printf(
"get_git_repository_for_file: %s\n", git_error_last()->message ); fflush( 0 );
385 if( git_repository_open( &repo, repo_path.ptr ) )
387 git_buf_free( &repo_path );
392 git_buf_free( &repo_path );
399 const wxTreeItemId& aParent,
400 std::vector<wxString>* aProjectNames,
404 wxFileName fn( aName );
407 return wxTreeItemId();
409 if( wxDirExists( aName ) )
411 type = TREE_FILE_TYPE::DIRECTORY;
417 bool addFile =
false;
419 for(
const wxString& m_filter :
m_filters )
421 wxCHECK2_MSG( reg.Compile( m_filter, wxRE_ICASE ),
continue,
422 wxString::Format(
"Regex %s failed to compile.", m_filter ) );
424 if( reg.Matches( aName ) )
432 return wxTreeItemId();
434 for(
int i =
static_cast<int>( TREE_FILE_TYPE::LEGACY_PROJECT );
435 i < static_cast<int>( TREE_FILE_TYPE::MAX ); i++ )
439 if( ext == wxT(
"" ) )
442 if( reg.Compile( wxString::FromAscii(
"^.*\\." ) + ext + wxString::FromAscii(
"$" ),
443 wxRE_ICASE ) && reg.Matches( aName ) )
451 wxString file = wxFileNameFromPath( aName );
452 wxFileName currfile( file );
456 if( ( type == TREE_FILE_TYPE::LEGACY_PROJECT )
457 && ( currfile.GetName().CmpNoCase(
project.GetName() ) == 0 ) )
459 return wxTreeItemId();
462 if( currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::LEGACY_SCHEMATIC )
463 || currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::SEXPR_SCHEMATIC ) )
468 return wxTreeItemId();
473 wxDir parentDir( parentTreeItem->
GetDir() );
474 std::vector<wxString> projects =
getProjects( parentDir );
477 return wxTreeItemId();
482 wxTreeItemIdValue cookie;
483 wxTreeItemId kid =
m_TreeProject->GetFirstChild( aParent, cookie );
490 return itemData->GetId();
496 if( type == TREE_FILE_TYPE::LEGACY_PROJECT || type == TREE_FILE_TYPE::JSON_PROJECT
497 || type == TREE_FILE_TYPE::LEGACY_SCHEMATIC || type == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
509 if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
513 case TREE_FILE_TYPE::LEGACY_PROJECT:
514 if( itemData->
GetType() == TREE_FILE_TYPE::JSON_PROJECT )
515 return wxTreeItemId();
519 case TREE_FILE_TYPE::LEGACY_SCHEMATIC:
520 if( itemData->
GetType() == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
521 return wxTreeItemId();
525 case TREE_FILE_TYPE::JSON_PROJECT:
526 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_PROJECT )
531 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
532 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_SCHEMATIC )
548 wxTreeItemId newItemId =
m_TreeProject->AppendItem( aParent, file );
555 wxString fileName = currfile.GetName().Lower();
556 wxString projName =
project.GetName().Lower();
558 if( fileName == projName || fileName.StartsWith( projName +
"-" ) )
562 bool subdir_populated =
false;
567 if( TREE_FILE_TYPE::DIRECTORY == type && aRecurse )
573 std::vector<wxString> projects =
getProjects( dir );
574 wxString dir_filename;
575 bool haveFile = dir.GetFirst( &dir_filename );
580 subdir_populated = aRecurse;
586 wxString
path = aName + wxFileName::GetPathSeparator() + dir_filename;
589 haveFile = dir.GetNext( &dir_filename );
598 if( subdir_populated )
624 wxFileName fn = pro_dir;
625 bool prjReset =
false;
636 bool prjOpened = fn.FileExists();
653 if( !prjOpened && !prjReset )
656 prjOpened = fn.FileExists();
663 m_root =
m_TreeProject->AddRoot( fn.GetFullName(),
static_cast<int>( TREE_FILE_TYPE::ROOT ),
664 static_cast<int>( TREE_FILE_TYPE::ROOT ) );
677 wxDir dir( pro_dir );
681 std::vector<wxString> projects =
getProjects( dir );
683 bool haveFile = dir.GetFirst( &filename );
687 if( filename != fn.GetFullName() )
689 wxString
name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
696 haveFile = dir.GetNext( &filename );
720 git_status_options opts;
721 git_status_init_options( &opts, GIT_STATUS_OPTIONS_VERSION );
723 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
724 opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
725 | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
727 git_status_list* status_list =
nullptr;
728 int error = git_status_list_new( &status_list, repo, &opts );
730 if( error != GIT_OK )
733 bool has_changed_files = git_status_list_entrycount( status_list ) > 0;
734 git_status_list_free( status_list );
735 return has_changed_files;
741 wxTreeItemId curr_item = Event.GetItem();
748 bool can_switch_to_project =
true;
749 bool can_create_new_directory =
true;
750 bool can_open_this_directory =
true;
751 bool can_edit =
true;
752 bool can_rename =
true;
753 bool can_delete =
true;
754 bool run_jobs =
false;
758 bool vcs_can_init = !vcs_has_repo;
759 bool vcs_can_remove = vcs_has_repo;
762 bool vcs_can_pull = vcs_can_fetch;
763 bool vcs_can_switch = vcs_has_repo;
767#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 )
768 int major, minor, rev;
769 bool libgit_init = ( git_libgit2_version( &major, &minor, &rev ) == GIT_OK );
772 bool libgit_init =
true;
775 vcs_menu &= libgit_init;
777 if( selection.size() == 0 )
781 if( selection.size() != 1 )
783 can_switch_to_project =
false;
784 can_create_new_directory =
false;
793 can_switch_to_project =
false;
799 can_delete = item->CanDelete();
800 can_rename = item->CanRename();
802 switch( item->GetType() )
804 case TREE_FILE_TYPE::JSON_PROJECT:
805 case TREE_FILE_TYPE::LEGACY_PROJECT:
810 can_switch_to_project =
false;
814 can_create_new_directory =
false;
815 can_open_this_directory =
false;
819 case TREE_FILE_TYPE::DIRECTORY:
820 can_switch_to_project =
false;
824 case TREE_FILE_TYPE::ZIP_ARCHIVE:
825 case TREE_FILE_TYPE::PDF:
827 can_switch_to_project =
false;
828 can_create_new_directory =
false;
829 can_open_this_directory =
false;
832 case TREE_FILE_TYPE::JOBSET_FILE:
837 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
838 case TREE_FILE_TYPE::SEXPR_PCB:
842 can_switch_to_project =
false;
843 can_create_new_directory =
false;
844 can_open_this_directory =
false;
854 if( can_switch_to_project )
857 _(
"Close all editors, and switch to the selected project" ),
858 KiBitmap( BITMAPS::open_project ) );
859 popup_menu.AppendSeparator();
862 if( can_create_new_directory )
865 _(
"Create a New Directory" ),
KiBitmap( BITMAPS::directory ) );
868 if( can_open_this_directory )
870 if( selection.size() == 1 )
873 text =
_(
"Reveal in Finder" );
874 help_text =
_(
"Reveals the directory in a Finder window" );
876 text =
_(
"Open Directory in File Explorer" );
877 help_text =
_(
"Opens the directory in the default system file manager" );
883 text =
_(
"Reveal in Finder" );
884 help_text =
_(
"Reveals the directories in a Finder window" );
886 text =
_(
"Open Directories in File Explorer" );
887 help_text =
_(
"Opens the directories in the default system file manager" );
892 KiBitmap( BITMAPS::directory_browser ) );
897 if( selection.size() == 1 )
898 help_text =
_(
"Open the file in a Text Editor" );
900 help_text =
_(
"Open files in a Text Editor" );
906 if( run_jobs && selection.size() == 1 )
914 if( selection.size() == 1 )
916 text =
_(
"Rename File..." );
917 help_text =
_(
"Rename file" );
921 text =
_(
"Rename Files..." );
922 help_text =
_(
"Rename files" );
931 if( selection.size() == 1 )
932 help_text =
_(
"Delete the file and its content" );
934 help_text =
_(
"Delete the files and their contents" );
936 if( can_switch_to_project
937 || can_create_new_directory
938 || can_open_this_directory
942 popup_menu.AppendSeparator();
956 wxMenu* vcs_submenu =
new wxMenu();
957 wxMenu* branch_submenu =
new wxMenu();
958 wxMenuItem* vcs_menuitem =
nullptr;
961 _(
"Add Project to Version Control..." ),
962 _(
"Initialize a new repository" ) );
963 vcs_menuitem->Enable( vcs_can_init );
967 _(
"Commit changes to the local repository" ) );
968 vcs_menuitem->Enable( vcs_can_commit );
970 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PUSH,
_(
"Push" ),
971 _(
"Push committed local changes to remote repository" ) );
972 vcs_menuitem->Enable( vcs_can_push );
974 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PULL,
_(
"Pull" ),
975 _(
"Pull changes from remote repository into local" ) );
976 vcs_menuitem->Enable( vcs_can_pull );
978 vcs_submenu->AppendSeparator();
981 _(
"Commit changes to the local repository" ) );
982 vcs_menuitem->Enable( vcs_can_commit );
984 vcs_submenu->AppendSeparator();
993 for(
size_t ii = 1; ii < branchNames.size() && ii < 6; ++ii )
995 wxString msg =
_(
"Switch to branch " ) + branchNames[ii];
997 vcs_menuitem->Enable( vcs_can_switch );
1001 _(
"Switch to a different branch" ) );
1002 vcs_menuitem->Enable( vcs_can_switch );
1006 vcs_submenu->AppendSeparator();
1008 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Remove Version Control" ),
1009 _(
"Delete all version control files from the project directory." ) );
1010 vcs_menuitem->Enable( vcs_can_remove );
1012 popup_menu.AppendSeparator();
1013 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
1016 if( popup_menu.GetMenuItemCount() > 0 )
1017 PopupMenu( &popup_menu );
1025 if( editorname.IsEmpty() )
1027 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1035 wxString fullFileName = item_data->GetFileName();
1037 if( !fullFileName.IsEmpty() )
1039 ExecuteFile( editorname, fullFileName.wc_str(),
nullptr,
false );
1050 item_data->Delete();
1060 if( tree_data.size() != 1 )
1064 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1065 tree_data[0]->GetFileName() );
1066 wxTextEntryDialog dlg( wxGetTopLevelParent(
this ), msg,
_(
"Change filename" ), buffer );
1068 if( dlg.ShowModal() != wxID_OK )
1071 buffer = dlg.GetValue();
1072 buffer.Trim(
true );
1073 buffer.Trim(
false );
1075 if( buffer.IsEmpty() )
1078 tree_data[0]->Rename( buffer,
true );
1087 if( tree_data.size() != 1 )
1124 wxTreeItemId itemId = Event.GetItem();
1130 if( tree_data->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1134 wxTreeItemIdValue cookie;
1135 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1138 bool subdir_populated =
false;
1141 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1145 if( !itemData || itemData->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1152 wxDir dir( fileName );
1154 if( dir.IsOpened() )
1156 std::vector<wxString> projects =
getProjects( dir );
1157 wxString dir_filename;
1158 bool haveFile = dir.GetFirst( &dir_filename );
1163 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1166 haveFile = dir.GetNext( &dir_filename );
1172 subdir_populated =
true;
1181 if( subdir_populated )
1189 wxArrayTreeItemIds selection;
1190 std::vector<PROJECT_TREE_ITEM*> data;
1194 for(
auto it = selection.begin(); it != selection.end(); it++ )
1200 wxLogTrace(
traceGit, wxS(
"Null tree item returned for selection, dynamic_cast "
1205 data.push_back( item );
1224 if( prj_dir == aSubDir )
1228 wxTreeItemIdValue cookie;
1229 wxTreeItemId root_id =
m_root;
1230 std::stack<wxTreeItemId> subdirs_id;
1232 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1236 if( ! child.IsOk() )
1238 if( subdirs_id.empty() )
1245 root_id = subdirs_id.top();
1256 if( itemData && ( itemData->
GetType() == TREE_FILE_TYPE::DIRECTORY ) )
1266 subdirs_id.push( child );
1282 const wxFileName& pathModified =
event.GetPath();
1283 wxString subdir = pathModified.GetPath();
1284 wxString fn = pathModified.GetFullPath();
1287 if( pathModified.GetFullName().IsEmpty() )
1289 subdir = subdir.BeforeLast(
'/' );
1290 fn = fn.BeforeLast(
'/' );
1293 switch( event.GetChangeType() )
1295 case wxFSW_EVENT_DELETE:
1296 case wxFSW_EVENT_CREATE:
1297 case wxFSW_EVENT_RENAME:
1301 case wxFSW_EVENT_MODIFY:
1304 case wxFSW_EVENT_ACCESS:
1311 if( !root_id.IsOk() )
1314 wxTreeItemIdValue cookie;
1315 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1317 switch( event.GetChangeType() )
1319 case wxFSW_EVENT_CREATE:
1334 case wxFSW_EVENT_DELETE:
1348 case wxFSW_EVENT_RENAME :
1350 const wxFileName& newpath =
event.GetNewPath();
1351 wxString newdir = newpath.GetPath();
1352 wxString newfn = newpath.GetFullPath();
1376 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1382 if( newitem.IsOk() )
1402#if defined( _WIN32 )
1435 fn.AssignDir( prj_dir );
1436 fn.DontFollowLink();
1450 TO_UTF8( fn.GetFullPath() ) );
1458 TO_UTF8( fn.GetFullPath() ) );
1463 if( m_TreeProject->IsEmpty() )
1467 wxTreeItemIdValue cookie;
1468 wxTreeItemId root_id = m_root;
1470 std::stack < wxTreeItemId > subdirs_id;
1472 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1473 int total_watch_count = 0;
1479 if( subdirs_id.empty() )
1485 root_id = subdirs_id.top();
1487 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1503 if( wxFileName::IsDirReadable(
path ) )
1508 fn.AssignDir(
path );
1509 m_watcher->Add( fn );
1510 total_watch_count++;
1514 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1515 subdirs_id.push( kid );
1519 kid = m_TreeProject->GetNextChild( root_id, cookie );
1526#if defined(DEBUG) && 1
1527 wxArrayString paths;
1528 m_watcher->GetWatchedPaths( &paths );
1531 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1565 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1566 wxPaintDC dc(
this );
1568 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1569 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1571 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1572 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1586 wxString dir = tree_data->
GetDir();
1590 wxLogError(
"Failed to initialize git project: project directory is empty." );
1595 git_repository* repo =
nullptr;
1596 int error = git_repository_open(&repo, dir.mb_str());
1601 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1604 _(
"The selected directory is already a git project." ) );
1605 git_repository_free( repo );
1611 dlg.SetTitle(
_(
"Set default remote" ) );
1617 error = git_repository_init( &repo, dir.mb_str(), 0 );
1621 git_repository_free( repo );
1627 git_error_last()->message );
1645 git_remote* remote =
nullptr;
1654 fullURL = dlg.
GetRepoURL().StartsWith(
"https" ) ?
"https://" :
"http://";
1662 fullURL.append( wxS(
":" ) );
1666 fullURL.append( wxS(
"@" ) );
1677 error = git_remote_create_with_fetchspec( &remote, repo,
"origin",
1678 fullURL.ToStdString().c_str(),
1679 "+refs/heads/*:refs/remotes/origin/*" );
1681 if( error != GIT_OK )
1687 git_error_last()->message );
1698 _(
"Fetching Remote" ),
1732 _(
"Fetching Remote" ),
1754 _(
"Fetching Remote" ),
1757 if( handler.
PerformPush() != PushResult::Success )
1770 if(
int error = git_reference_name_to_id( &head_oid, aRepo,
"HEAD" ) != 0 )
1772 wxLogError(
"Failed to lookup HEAD reference" );
1777 git_commit* commit =
nullptr;
1778 if(
int error = git_commit_lookup( &commit, aRepo, &head_oid ) != GIT_OK )
1780 wxLogError(
"Failed to lookup commit" );
1784 git_reference* branchRef =
nullptr;
1786 if( git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ) != 0 )
1788 wxLogError(
"Failed to create branch" );
1789 git_commit_free( commit );
1793 git_commit_free( commit );
1794 git_reference_free( branchRef );
1807 wxString branchName;
1816 if( retval == wxID_ADD )
1818 else if( retval != wxID_OK )
1826 if( branchIndex < 0 ||
static_cast<size_t>( branchIndex ) >= branches.size() )
1829 branchName = branches[branchIndex];
1833 git_reference* branchRef =
nullptr;
1835 if( git_reference_lookup( &branchRef, repo, branchName.mb_str() ) != GIT_OK &&
1836 git_reference_dwim( &branchRef, repo, branchName.mb_str() ) != GIT_OK )
1838 wxString errorMessage = wxString::Format(
_(
"Failed to lookup branch '%s': %s" ),
1839 branchName, giterr_last()->message );
1844 const char* branchRefName = git_reference_name( branchRef );
1846 git_object* branchObj =
nullptr;
1848 if( git_revparse_single( &branchObj, repo, branchName.mb_str() ) != 0 )
1850 wxString errorMessage =
1851 wxString::Format(
_(
"Failed to find branch head for '%s'" ), branchName );
1853 git_reference_free( branchRef );
1859 if( git_checkout_tree( repo, branchObj,
nullptr ) != 0 )
1861 wxString errorMessage =
1862 wxString::Format(
_(
"Failed to switch to branch '%s'" ), branchName );
1864 git_reference_free( branchRef );
1865 git_object_free( branchObj );
1870 if( git_repository_set_head( repo, branchRefName ) != 0 )
1872 wxString errorMessage = wxString::Format(
1873 _(
"Failed to update HEAD reference for branch '%s'" ), branchName );
1875 git_reference_free( branchRef );
1876 git_object_free( branchObj );
1881 git_reference_free( branchRef );
1882 git_object_free( branchObj );
1891 || !
IsOK( wxGetTopLevelParent(
this ),
1892 _(
"Are you sure you want to remove git tracking from this project?" ) ) )
1898 git_repository_free( repo );
1903 fn.AppendDir(
".git" );
1913 std::stack<wxTreeItemId> items;
1916 while( !items.empty() )
1918 wxTreeItemId current = items.top();
1922 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
1924 wxTreeItemIdValue cookie;
1925 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
1927 while( child.IsOk() )
1929 items.push( child );
1946 if( timeSinceLastUpdate.Abs() < wxTimeSpan::Seconds( 2 ) )
1963 git_reference* currentBranchReference =
nullptr;
1964 int rc = git_repository_head( ¤tBranchReference, repo );
1967 wxFileName rootFilename( rootItem->
GetFileName() );
1968 wxString repoWorkDir( git_repository_workdir( repo ) );
1971 if( currentBranchReference )
1973 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
1974 wxString branchName = git_reference_shorthand( currentBranchReference );
1976 m_TreeProject->SetItemText( kid, filename +
" [" + branchName +
"]" );
1977 git_reference_free( currentBranchReference );
1979 else if( rc == GIT_EUNBORNBRANCH )
1987 wxLogError(
"Failed to lookup current branch: %s", giterr_last()->message );
1993 std::map<wxString, wxTreeItemId> branchMap;
1995 std::stack<wxTreeItemId> items;
1998 while( !items.empty() )
2007 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2009 branchMap[gitAbsPath] = kid;
2011 wxTreeItemIdValue cookie;
2012 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
2014 while( child.IsOk() )
2016 items.push( child );
2022 wxFileName relative = rootFilename;
2023 relative.MakeRelativeTo( repoWorkDir );
2024 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
2027 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
2030 const char* pathspec[] = { pathspecStr.c_str().AsChar() };
2032 git_status_options status_options;
2033 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2034 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2035 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
2036 status_options.pathspec = { (
char**) pathspec, 1 };
2038 git_index* index =
nullptr;
2040 if( git_repository_index( &index, repo ) != GIT_OK )
2043 wxLogTrace(
traceGit, wxS(
"Failed to get git index: %s" ), giterr_last()->message );
2047 git_status_list* status_list =
nullptr;
2049 if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
2051 wxLogTrace(
traceGit, wxS(
"Failed to get git status list: %s" ), giterr_last()->message );
2052 git_index_free( index );
2058 size_t count = git_status_list_entrycount( status_list );
2060 for(
size_t ii = 0; ii < count; ++ii )
2062 const git_status_entry* entry = git_status_byindex( status_list, ii );
2063 std::string
path( entry->head_to_index? entry->head_to_index->old_file.path
2064 : entry->index_to_workdir->old_file.path );
2066 wxString absPath = repoWorkDir;
2069 auto iter = branchMap.find( absPath );
2071 if( iter == branchMap.end() )
2077 if( entry->status == GIT_STATUS_CURRENT )
2083 else if( entry->status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
2090 else if( entry->status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
2097 else if( entry->status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
2106 if( localChanges.count(
path ) )
2113 else if( remoteChanges.count(
path ) )
2129 git_status_list_free( status_list );
2130 git_index_free( index );
2140 if( repo ==
nullptr )
2142 wxMessageBox(
"The selected directory is not a git project." );
2146 git_config*
config =
nullptr;
2147 git_repository_config( &
config, repo );
2150 wxString authorName;
2151 wxString authorEmail;
2154 git_config_entry* name_c =
nullptr;
2155 git_config_entry* email_c =
nullptr;
2156 int authorNameError = git_config_get_entry( &name_c,
config,
"user.name" );
2158 if( authorNameError != 0 || name_c ==
nullptr )
2164 authorName = name_c->value;
2165 git_config_entry_free( name_c );
2169 int authorEmailError = git_config_get_entry( &email_c,
config,
"user.email" );
2171 if( authorEmailError != 0 || email_c ==
nullptr )
2177 authorEmail = email_c->value;
2178 git_config_entry_free( email_c );
2182 git_config_free(
config );
2185 git_status_options status_options;
2186 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2187 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2188 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2190 git_status_list* status_list =
nullptr;
2191 git_status_list_new( &status_list, repo, &status_options );
2193 std::map<wxString, int> modifiedFiles;
2195 size_t count = git_status_list_entrycount( status_list );
2197 std::set<wxString> selected_files;
2201 if( item->GetType() != TREE_FILE_TYPE::DIRECTORY )
2202 selected_files.emplace( item->GetFileName() );
2205 for(
size_t i = 0; i < count; ++i )
2207 const git_status_entry* entry = git_status_byindex( status_list, i );
2210 if( entry->status == GIT_STATUS_CURRENT
2211 || ( entry->status & ( GIT_STATUS_CONFLICTED | GIT_STATUS_IGNORED ) ) )
2222 if( entry->index_to_workdir )
2224 fn.Assign( entry->index_to_workdir->old_file.path );
2225 fn.MakeAbsolute( git_repository_workdir( repo ) );
2226 filePath = wxString( entry->index_to_workdir->old_file.path, wxConvUTF8 );
2228 else if( entry->head_to_index )
2230 fn.Assign( entry->head_to_index->old_file.path );
2231 fn.MakeAbsolute( git_repository_workdir( repo ) );
2232 filePath = wxString( entry->head_to_index->old_file.path, wxConvUTF8 );
2236 wxCHECK2_MSG(
false,
continue,
"File status with neither git_status_entry set!" );
2241 modifiedFiles.emplace( filePath, entry->status );
2243 else if( selected_files.count( fn.GetFullPath() ) )
2245 modifiedFiles.emplace( filePath, entry->status );
2249 git_status_list_free( status_list );
2252 DIALOG_GIT_COMMIT dlg( wxGetTopLevelParent(
this ), repo, authorName, authorEmail,
2256 if( ret == wxID_OK )
2260 git_tree* tree =
nullptr;
2261 git_commit* parent =
nullptr;
2262 git_index* index =
nullptr;
2268 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2274 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2278 if( git_repository_index( &index, repo ) != 0 )
2280 wxMessageBox( wxString::Format(
_(
"Failed to get repository index: %s" ),
2281 giterr_last()->message ) );
2285 for( wxString& file :files )
2287 if( git_index_add_bypath( index, file.mb_str() ) != 0 )
2289 wxMessageBox( wxString::Format(
_(
"Failed to add file to index: %s" ),
2290 giterr_last()->message ) );
2291 git_index_free( index );
2296 if( git_index_write( index ) != 0 )
2298 wxMessageBox( wxString::Format(
_(
"Failed to write index: %s" ),
2299 giterr_last()->message ) );
2300 git_index_free( index );
2304 if( git_index_write_tree( &tree_id, index ) != 0)
2306 wxMessageBox( wxString::Format(
_(
"Failed to write tree: %s" ),
2307 giterr_last()->message ) );
2308 git_index_free( index );
2312 git_index_free( index );
2314 if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
2316 wxMessageBox( wxString::Format(
_(
"Failed to lookup tree: %s" ),
2317 giterr_last()->message ) );
2321 git_reference* headRef =
nullptr;
2323 if( 0 == git_repository_head_unborn( repo ) )
2325 if( git_repository_head( &headRef, repo ) != 0 )
2327 wxMessageBox( wxString::Format(
_(
"Failed to get HEAD reference: %s" ),
2328 giterr_last()->message ) );
2329 git_index_free( index );
2333 if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
2335 wxMessageBox( wxString::Format(
_(
"Failed to get commit: %s" ),
2336 giterr_last()->message ) );
2337 git_reference_free( headRef );
2338 git_index_free( index );
2342 git_reference_free( headRef );
2349 git_signature* author =
nullptr;
2351 if( git_signature_now( &author, author_name.mb_str(), author_email.mb_str() ) != 0 )
2353 wxMessageBox( wxString::Format(
_(
"Failed to create author signature: %s" ),
2354 giterr_last()->message ) );
2360#if( LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR == 8 \
2361 && ( LIBGIT2_VER_REVISION < 2 || LIBGIT2_VER_REVISION == 3 ) )
2380 git_commit*
const parents[1] = { parent };
2383 const git_commit* parents[1] = { parent };
2386 if( git_commit_create( &oid, repo,
"HEAD", author, author,
nullptr, commit_msg.mb_str(), tree,
2389 wxMessageBox( wxString::Format(
_(
"Failed to create commit: %s" ),
2390 giterr_last()->message ) );
2394 git_signature_free( author );
2395 git_commit_free( parent );
2396 git_tree_free( tree );
2417 if( git_repository_index( &index, repo ) != 0 )
2421 if( git_index_find( &entry_pos, index, aFile.mb_str() ) == 0 )
2423 git_index_free( index );
2427 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.
EVT_MENU_RANGE(ID_GERBVIEW_DRILL_FILE1, ID_GERBVIEW_DRILL_FILEMAX, GERBVIEW_FRAME::OnDrlFileHistory) EVT_MENU_RANGE(ID_GERBVIEW_ZIP_FILE1
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.