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 wxFileName prj_dir(
Prj().GetProjectPath(), wxEmptyString );
750 prj_dir.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_CASE | wxPATH_NORM_DOTS
751 | wxPATH_NORM_ENV_VARS | wxPATH_NORM_TILDE );
752 git_dir.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_CASE | wxPATH_NORM_DOTS
753 | wxPATH_NORM_ENV_VARS | wxPATH_NORM_TILDE );
754 wxString prj_name = prj_dir.GetFullPath();
755 wxString git_name = git_dir.GetFullPath();
757 bool can_switch_to_project =
true;
758 bool can_create_new_directory =
true;
759 bool can_open_this_directory =
true;
760 bool can_edit =
true;
761 bool can_rename =
true;
762 bool can_delete =
true;
763 bool run_jobs =
false;
767 bool vcs_can_init = !vcs_has_repo;
768 bool vcs_can_remove = vcs_has_repo && git_name.StartsWith( prj_name );
771 bool vcs_can_pull = vcs_can_fetch;
772 bool vcs_can_switch = vcs_has_repo;
776#if ( LIBGIT2_VER_MAJOR >= 1 ) || ( LIBGIT2_VER_MINOR >= 99 )
777 int major, minor, rev;
778 bool libgit_init = ( git_libgit2_version( &major, &minor, &rev ) == GIT_OK );
781 bool libgit_init =
true;
784 vcs_menu &= libgit_init;
786 if( selection.size() == 0 )
790 if( selection.size() != 1 )
792 can_switch_to_project =
false;
793 can_create_new_directory =
false;
802 can_switch_to_project =
false;
808 can_delete = item->CanDelete();
809 can_rename = item->CanRename();
811 switch( item->GetType() )
813 case TREE_FILE_TYPE::JSON_PROJECT:
814 case TREE_FILE_TYPE::LEGACY_PROJECT:
819 can_switch_to_project =
false;
823 can_create_new_directory =
false;
824 can_open_this_directory =
false;
828 case TREE_FILE_TYPE::DIRECTORY:
829 can_switch_to_project =
false;
833 case TREE_FILE_TYPE::ZIP_ARCHIVE:
834 case TREE_FILE_TYPE::PDF:
836 can_switch_to_project =
false;
837 can_create_new_directory =
false;
838 can_open_this_directory =
false;
841 case TREE_FILE_TYPE::JOBSET_FILE:
846 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
847 case TREE_FILE_TYPE::SEXPR_PCB:
851 can_switch_to_project =
false;
852 can_create_new_directory =
false;
853 can_open_this_directory =
false;
863 if( can_switch_to_project )
866 _(
"Close all editors, and switch to the selected project" ),
867 KiBitmap( BITMAPS::open_project ) );
868 popup_menu.AppendSeparator();
871 if( can_create_new_directory )
874 _(
"Create a New Directory" ),
KiBitmap( BITMAPS::directory ) );
877 if( can_open_this_directory )
879 if( selection.size() == 1 )
882 text =
_(
"Reveal in Finder" );
883 help_text =
_(
"Reveals the directory in a Finder window" );
885 text =
_(
"Open Directory in File Explorer" );
886 help_text =
_(
"Opens the directory in the default system file manager" );
892 text =
_(
"Reveal in Finder" );
893 help_text =
_(
"Reveals the directories in a Finder window" );
895 text =
_(
"Open Directories in File Explorer" );
896 help_text =
_(
"Opens the directories in the default system file manager" );
901 KiBitmap( BITMAPS::directory_browser ) );
906 if( selection.size() == 1 )
907 help_text =
_(
"Open the file in a Text Editor" );
909 help_text =
_(
"Open files in a Text Editor" );
915 if( run_jobs && selection.size() == 1 )
923 if( selection.size() == 1 )
925 text =
_(
"Rename File..." );
926 help_text =
_(
"Rename file" );
930 text =
_(
"Rename Files..." );
931 help_text =
_(
"Rename files" );
940 if( selection.size() == 1 )
941 help_text =
_(
"Delete the file and its content" );
943 help_text =
_(
"Delete the files and their contents" );
945 if( can_switch_to_project
946 || can_create_new_directory
947 || can_open_this_directory
951 popup_menu.AppendSeparator();
965 wxMenu* vcs_submenu =
new wxMenu();
966 wxMenu* branch_submenu =
new wxMenu();
967 wxMenuItem* vcs_menuitem =
nullptr;
970 _(
"Add Project to Version Control..." ),
971 _(
"Initialize a new repository" ) );
972 vcs_menuitem->Enable( vcs_can_init );
976 _(
"Commit changes to the local repository" ) );
977 vcs_menuitem->Enable( vcs_can_commit );
979 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PUSH,
_(
"Push" ),
980 _(
"Push committed local changes to remote repository" ) );
981 vcs_menuitem->Enable( vcs_can_push );
983 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PULL,
_(
"Pull" ),
984 _(
"Pull changes from remote repository into local" ) );
985 vcs_menuitem->Enable( vcs_can_pull );
987 vcs_submenu->AppendSeparator();
990 _(
"Commit changes to the local repository" ) );
991 vcs_menuitem->Enable( vcs_can_commit );
993 vcs_submenu->AppendSeparator();
1002 for(
size_t ii = 1; ii < branchNames.size() && ii < 6; ++ii )
1004 wxString msg =
_(
"Switch to branch " ) + branchNames[ii];
1006 vcs_menuitem->Enable( vcs_can_switch );
1010 _(
"Switch to a different branch" ) );
1011 vcs_menuitem->Enable( vcs_can_switch );
1015 vcs_submenu->AppendSeparator();
1017 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Remove Version Control" ),
1018 _(
"Delete all version control files from the project directory." ) );
1019 vcs_menuitem->Enable( vcs_can_remove );
1021 popup_menu.AppendSeparator();
1022 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
1025 if( popup_menu.GetMenuItemCount() > 0 )
1026 PopupMenu( &popup_menu );
1034 if( editorname.IsEmpty() )
1036 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1044 wxString fullFileName = item_data->GetFileName();
1046 if( !fullFileName.IsEmpty() )
1048 ExecuteFile( editorname, fullFileName.wc_str(),
nullptr,
false );
1059 item_data->Delete();
1069 if( tree_data.size() != 1 )
1073 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1074 tree_data[0]->GetFileName() );
1075 wxTextEntryDialog dlg( wxGetTopLevelParent(
this ), msg,
_(
"Change filename" ), buffer );
1077 if( dlg.ShowModal() != wxID_OK )
1080 buffer = dlg.GetValue();
1081 buffer.Trim(
true );
1082 buffer.Trim(
false );
1084 if( buffer.IsEmpty() )
1087 tree_data[0]->Rename( buffer,
true );
1096 if( tree_data.size() != 1 )
1133 wxTreeItemId itemId = Event.GetItem();
1139 if( tree_data->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1143 wxTreeItemIdValue cookie;
1144 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1147 bool subdir_populated =
false;
1150 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1154 if( !itemData || itemData->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1161 wxDir dir( fileName );
1163 if( dir.IsOpened() )
1165 std::vector<wxString> projects =
getProjects( dir );
1166 wxString dir_filename;
1167 bool haveFile = dir.GetFirst( &dir_filename );
1172 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1175 haveFile = dir.GetNext( &dir_filename );
1181 subdir_populated =
true;
1190 if( subdir_populated )
1198 wxArrayTreeItemIds selection;
1199 std::vector<PROJECT_TREE_ITEM*> data;
1203 for(
auto it = selection.begin(); it != selection.end(); it++ )
1209 wxLogTrace(
traceGit, wxS(
"Null tree item returned for selection, dynamic_cast "
1214 data.push_back( item );
1233 if( prj_dir == aSubDir )
1237 wxTreeItemIdValue cookie;
1238 wxTreeItemId root_id =
m_root;
1239 std::stack<wxTreeItemId> subdirs_id;
1241 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1245 if( ! child.IsOk() )
1247 if( subdirs_id.empty() )
1254 root_id = subdirs_id.top();
1265 if( itemData && ( itemData->
GetType() == TREE_FILE_TYPE::DIRECTORY ) )
1275 subdirs_id.push( child );
1291 const wxFileName& pathModified =
event.GetPath();
1292 wxString subdir = pathModified.GetPath();
1293 wxString fn = pathModified.GetFullPath();
1296 if( pathModified.GetFullName().IsEmpty() )
1298 subdir = subdir.BeforeLast(
'/' );
1299 fn = fn.BeforeLast(
'/' );
1302 switch( event.GetChangeType() )
1304 case wxFSW_EVENT_DELETE:
1305 case wxFSW_EVENT_CREATE:
1306 case wxFSW_EVENT_RENAME:
1310 case wxFSW_EVENT_MODIFY:
1313 case wxFSW_EVENT_ACCESS:
1320 if( !root_id.IsOk() )
1323 wxTreeItemIdValue cookie;
1324 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1326 switch( event.GetChangeType() )
1328 case wxFSW_EVENT_CREATE:
1343 case wxFSW_EVENT_DELETE:
1357 case wxFSW_EVENT_RENAME :
1359 const wxFileName& newpath =
event.GetNewPath();
1360 wxString newdir = newpath.GetPath();
1361 wxString newfn = newpath.GetFullPath();
1385 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1391 if( newitem.IsOk() )
1411#if defined( _WIN32 )
1444 fn.AssignDir( prj_dir );
1445 fn.DontFollowLink();
1459 TO_UTF8( fn.GetFullPath() ) );
1467 TO_UTF8( fn.GetFullPath() ) );
1472 if( m_TreeProject->IsEmpty() )
1476 wxTreeItemIdValue cookie;
1477 wxTreeItemId root_id = m_root;
1479 std::stack < wxTreeItemId > subdirs_id;
1481 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1482 int total_watch_count = 0;
1488 if( subdirs_id.empty() )
1494 root_id = subdirs_id.top();
1496 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1512 if( wxFileName::IsDirReadable(
path ) )
1517 fn.AssignDir(
path );
1518 m_watcher->Add( fn );
1519 total_watch_count++;
1523 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1524 subdirs_id.push( kid );
1528 kid = m_TreeProject->GetNextChild( root_id, cookie );
1535#if defined(DEBUG) && 1
1536 wxArrayString paths;
1537 m_watcher->GetWatchedPaths( &paths );
1540 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1574 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1575 wxPaintDC dc(
this );
1577 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1578 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1580 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1581 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1595 wxString dir = tree_data->
GetDir();
1599 wxLogError(
"Failed to initialize git project: project directory is empty." );
1604 git_repository* repo =
nullptr;
1605 int error = git_repository_open(&repo, dir.mb_str());
1610 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1613 _(
"The selected directory is already a Git project." ) );
1614 git_repository_free( repo );
1620 dlg.SetTitle(
_(
"Set default remote" ) );
1626 error = git_repository_init( &repo, dir.mb_str(), 0 );
1630 git_repository_free( repo );
1636 git_error_last()->message );
1654 git_remote* remote =
nullptr;
1663 fullURL = dlg.
GetRepoURL().StartsWith(
"https" ) ?
"https://" :
"http://";
1671 fullURL.append( wxS(
":" ) );
1675 fullURL.append( wxS(
"@" ) );
1686 error = git_remote_create_with_fetchspec( &remote, repo,
"origin",
1687 fullURL.ToStdString().c_str(),
1688 "+refs/heads/*:refs/remotes/origin/*" );
1690 if( error != GIT_OK )
1696 git_error_last()->message );
1707 _(
"Fetching Remote" ),
1741 _(
"Fetching Remote" ),
1763 _(
"Fetching Remote" ),
1766 if( handler.
PerformPush() != PushResult::Success )
1779 if(
int error = git_reference_name_to_id( &head_oid, aRepo,
"HEAD" ) != 0 )
1781 wxLogError(
"Failed to lookup HEAD reference" );
1786 git_commit* commit =
nullptr;
1787 if(
int error = git_commit_lookup( &commit, aRepo, &head_oid ) != GIT_OK )
1789 wxLogError(
"Failed to lookup commit" );
1793 git_reference* branchRef =
nullptr;
1795 if( git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ) != 0 )
1797 wxLogError(
"Failed to create branch" );
1798 git_commit_free( commit );
1802 git_commit_free( commit );
1803 git_reference_free( branchRef );
1816 wxString branchName;
1825 if( retval == wxID_ADD )
1827 else if( retval != wxID_OK )
1835 if( branchIndex < 0 ||
static_cast<size_t>( branchIndex ) >= branches.size() )
1838 branchName = branches[branchIndex];
1842 git_reference* branchRef =
nullptr;
1844 if( git_reference_lookup( &branchRef, repo, branchName.mb_str() ) != GIT_OK &&
1845 git_reference_dwim( &branchRef, repo, branchName.mb_str() ) != GIT_OK )
1847 wxString errorMessage = wxString::Format(
_(
"Failed to lookup branch '%s': %s" ),
1848 branchName, giterr_last()->message );
1853 const char* branchRefName = git_reference_name( branchRef );
1855 git_object* branchObj =
nullptr;
1857 if( git_revparse_single( &branchObj, repo, branchName.mb_str() ) != 0 )
1859 wxString errorMessage =
1860 wxString::Format(
_(
"Failed to find branch head for '%s'" ), branchName );
1862 git_reference_free( branchRef );
1868 if( git_checkout_tree( repo, branchObj,
nullptr ) != 0 )
1870 wxString errorMessage =
1871 wxString::Format(
_(
"Failed to switch to branch '%s'" ), branchName );
1873 git_reference_free( branchRef );
1874 git_object_free( branchObj );
1879 if( git_repository_set_head( repo, branchRefName ) != 0 )
1881 wxString errorMessage = wxString::Format(
1882 _(
"Failed to update HEAD reference for branch '%s'" ), branchName );
1884 git_reference_free( branchRef );
1885 git_object_free( branchObj );
1890 git_reference_free( branchRef );
1891 git_object_free( branchObj );
1900 || !
IsOK( wxGetTopLevelParent(
this ),
1901 _(
"Are you sure you want to remove Git tracking from this project?" ) ) )
1907 git_repository_free( repo );
1912 fn.AppendDir(
".git" );
1922 std::stack<wxTreeItemId> items;
1925 while( !items.empty() )
1927 wxTreeItemId current = items.top();
1931 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
1933 wxTreeItemIdValue cookie;
1934 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
1936 while( child.IsOk() )
1938 items.push( child );
1955 if( timeSinceLastUpdate.Abs() < wxTimeSpan::Seconds( 2 ) )
1972 git_reference* currentBranchReference =
nullptr;
1973 int rc = git_repository_head( ¤tBranchReference, repo );
1976 wxFileName rootFilename( rootItem->
GetFileName() );
1977 wxString repoWorkDir( git_repository_workdir( repo ) );
1980 if( currentBranchReference )
1982 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
1983 wxString branchName = git_reference_shorthand( currentBranchReference );
1985 m_TreeProject->SetItemText( kid, filename +
" [" + branchName +
"]" );
1986 git_reference_free( currentBranchReference );
1988 else if( rc == GIT_EUNBORNBRANCH )
1996 wxLogError(
"Failed to lookup current branch: %s", giterr_last()->message );
2002 std::map<wxString, wxTreeItemId> branchMap;
2004 std::stack<wxTreeItemId> items;
2007 while( !items.empty() )
2016 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2018 branchMap[gitAbsPath] = kid;
2020 wxTreeItemIdValue cookie;
2021 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
2023 while( child.IsOk() )
2025 items.push( child );
2031 wxFileName relative = rootFilename;
2032 relative.MakeRelativeTo( repoWorkDir );
2033 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
2036 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
2039 const char* pathspec[] = { pathspecStr.c_str().AsChar() };
2041 git_status_options status_options;
2042 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2043 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2044 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
2045 status_options.pathspec = { (
char**) pathspec, 1 };
2047 git_index* index =
nullptr;
2049 if( git_repository_index( &index, repo ) != GIT_OK )
2052 wxLogTrace(
traceGit, wxS(
"Failed to get git index: %s" ), giterr_last()->message );
2056 git_status_list* status_list =
nullptr;
2058 if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
2060 wxLogTrace(
traceGit, wxS(
"Failed to get git status list: %s" ), giterr_last()->message );
2061 git_index_free( index );
2067 size_t count = git_status_list_entrycount( status_list );
2069 for(
size_t ii = 0; ii < count; ++ii )
2071 const git_status_entry* entry = git_status_byindex( status_list, ii );
2072 std::string
path( entry->head_to_index? entry->head_to_index->old_file.path
2073 : entry->index_to_workdir->old_file.path );
2075 wxString absPath = repoWorkDir;
2078 auto iter = branchMap.find( absPath );
2080 if( iter == branchMap.end() )
2086 if( entry->status == GIT_STATUS_CURRENT )
2092 else if( entry->status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
2099 else if( entry->status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
2106 else if( entry->status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
2115 if( localChanges.count(
path ) )
2122 else if( remoteChanges.count(
path ) )
2138 git_status_list_free( status_list );
2139 git_index_free( index );
2149 if( repo ==
nullptr )
2151 wxMessageBox(
_(
"The selected directory is not a Git project." ) );
2155 git_config*
config =
nullptr;
2156 git_repository_config( &
config, repo );
2159 wxString authorName;
2160 wxString authorEmail;
2163 git_config_entry* name_c =
nullptr;
2164 git_config_entry* email_c =
nullptr;
2165 int authorNameError = git_config_get_entry( &name_c,
config,
"user.name" );
2167 if( authorNameError != 0 || name_c ==
nullptr )
2173 authorName = name_c->value;
2174 git_config_entry_free( name_c );
2178 int authorEmailError = git_config_get_entry( &email_c,
config,
"user.email" );
2180 if( authorEmailError != 0 || email_c ==
nullptr )
2186 authorEmail = email_c->value;
2187 git_config_entry_free( email_c );
2191 git_config_free(
config );
2194 git_status_options status_options;
2195 git_status_init_options( &status_options, GIT_STATUS_OPTIONS_VERSION );
2196 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2197 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2199 git_status_list* status_list =
nullptr;
2200 git_status_list_new( &status_list, repo, &status_options );
2202 std::map<wxString, int> modifiedFiles;
2204 size_t count = git_status_list_entrycount( status_list );
2206 std::set<wxString> selected_files;
2210 if( item->GetType() != TREE_FILE_TYPE::DIRECTORY )
2211 selected_files.emplace( item->GetFileName() );
2214 for(
size_t i = 0; i < count; ++i )
2216 const git_status_entry* entry = git_status_byindex( status_list, i );
2219 if( entry->status == GIT_STATUS_CURRENT
2220 || ( entry->status & ( GIT_STATUS_CONFLICTED | GIT_STATUS_IGNORED ) ) )
2231 if( entry->index_to_workdir )
2233 fn.Assign( entry->index_to_workdir->old_file.path );
2234 fn.MakeAbsolute( git_repository_workdir( repo ) );
2235 filePath = wxString( entry->index_to_workdir->old_file.path, wxConvUTF8 );
2237 else if( entry->head_to_index )
2239 fn.Assign( entry->head_to_index->old_file.path );
2240 fn.MakeAbsolute( git_repository_workdir( repo ) );
2241 filePath = wxString( entry->head_to_index->old_file.path, wxConvUTF8 );
2245 wxCHECK2_MSG(
false,
continue,
"File status with neither git_status_entry set!" );
2250 wxString fileName = fn.GetFullPath();
2252 if( !fileName.StartsWith( projectPath ) )
2268 if( fn.GetPath().Contains(
Prj().GetProjectName() + wxT(
"-backups" ) ) )
2273 modifiedFiles.emplace( filePath, entry->status );
2275 else if( selected_files.count( fn.GetFullPath() ) )
2277 modifiedFiles.emplace( filePath, entry->status );
2281 git_status_list_free( status_list );
2284 DIALOG_GIT_COMMIT dlg( wxGetTopLevelParent(
this ), repo, authorName, authorEmail,
2288 if( ret == wxID_OK )
2292 git_tree* tree =
nullptr;
2293 git_commit* parent =
nullptr;
2294 git_index* index =
nullptr;
2300 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2306 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2310 if( git_repository_index( &index, repo ) != 0 )
2312 wxMessageBox( wxString::Format(
_(
"Failed to get repository index: %s" ),
2313 giterr_last()->message ) );
2317 for( wxString& file :files )
2319 if( git_index_add_bypath( index, file.mb_str() ) != 0 )
2321 wxMessageBox( wxString::Format(
_(
"Failed to add file to index: %s" ),
2322 giterr_last()->message ) );
2323 git_index_free( index );
2328 if( git_index_write( index ) != 0 )
2330 wxMessageBox( wxString::Format(
_(
"Failed to write index: %s" ),
2331 giterr_last()->message ) );
2332 git_index_free( index );
2336 if( git_index_write_tree( &tree_id, index ) != 0)
2338 wxMessageBox( wxString::Format(
_(
"Failed to write tree: %s" ),
2339 giterr_last()->message ) );
2340 git_index_free( index );
2344 git_index_free( index );
2346 if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
2348 wxMessageBox( wxString::Format(
_(
"Failed to lookup tree: %s" ),
2349 giterr_last()->message ) );
2353 git_reference* headRef =
nullptr;
2355 if( 0 == git_repository_head_unborn( repo ) )
2357 if( git_repository_head( &headRef, repo ) != 0 )
2359 wxMessageBox( wxString::Format(
_(
"Failed to get HEAD reference: %s" ),
2360 giterr_last()->message ) );
2361 git_index_free( index );
2365 if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
2367 wxMessageBox( wxString::Format(
_(
"Failed to get commit: %s" ),
2368 giterr_last()->message ) );
2369 git_reference_free( headRef );
2370 git_index_free( index );
2374 git_reference_free( headRef );
2381 git_signature* author =
nullptr;
2383 if( git_signature_now( &author, author_name.mb_str(), author_email.mb_str() ) != 0 )
2385 wxMessageBox( wxString::Format(
_(
"Failed to create author signature: %s" ),
2386 giterr_last()->message ) );
2392#if( LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR == 8 \
2393 && ( LIBGIT2_VER_REVISION < 2 || LIBGIT2_VER_REVISION == 3 ) )
2412 git_commit*
const parents[1] = { parent };
2415 const git_commit* parents[1] = { parent };
2418 if( git_commit_create( &oid, repo,
"HEAD", author, author,
nullptr, commit_msg.mb_str(), tree,
2421 wxMessageBox( wxString::Format(
_(
"Failed to create commit: %s" ),
2422 giterr_last()->message ) );
2426 git_signature_free( author );
2427 git_commit_free( parent );
2428 git_tree_free( tree );
2449 if( git_repository_index( &index, repo ) != 0 )
2453 if( git_index_find( &entry_pos, index, aFile.mb_str() ) == 0 )
2455 git_index_free( index );
2459 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 GetGitRootDirectory() const
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 const wxString GetProjectPath() const
Return the full path of the project.
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 LockFileExtension
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 LockFilePrefix
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 BackupFileSuffix
static const std::string AutoSaveFilePrefix
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.