30#include <wx/stdpaths.h>
33#include <wx/textdlg.h>
52#include <wx/dcclient.h>
53#include <wx/settings.h>
96 wxT(
"^.*\\.kicad_pro$" ),
99 wxT(
"^.*\\.kicad_sch$" ),
100 wxT(
"^[^$].*\\.brd$" ),
101 wxT(
"^[^$].*\\.kicad_pcb$" ),
102 wxT(
"^[^$].*\\.kicad_dru$" ),
103 wxT(
"^[^$].*\\.kicad_wks$" ),
104 wxT(
"^[^$].*\\.kicad_mod$" ),
108 wxT(
"^.*\\.kicad_sym$" ),
113 wxT(
"^.*\\.gbrjob$" ),
114 wxT(
"^.*\\.gb[alops]$" ),
115 wxT(
"^.*\\.gt[alops]$" ),
116 wxT(
"^.*\\.g[0-9]{1,2}$" ),
117 wxT(
"^.*\\.gm[0-9]{1,2}$" ),
121 wxT(
"^.*\\.html$" ),
176 wxSashLayoutWindow( parent,
ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize,
177 wxNO_BORDER | wxTAB_TRAVERSAL )
180 m_TreeProject =
nullptr;
181 m_isRenaming =
false;
182 m_selectedItem =
nullptr;
183 m_watcherNeedReset =
false;
184 m_lastGitStatusUpdate = wxDateTime::Now();
185 m_gitLastError = GIT_ERROR_NONE;
188 Connect( wxEVT_FSWATCHER,
191 Bind( wxEVT_SYS_COLOUR_CHANGED,
201 m_filters.emplace_back( wxT(
"^no KiCad files found" ) );
230 if( tree_data.size() != 1 )
233 wxString prj_filename = tree_data[0]->GetFileName();
247 wxString curr_dir = item_data->GetDir();
249 if( curr_dir.IsEmpty() )
255 if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
258 if( !curr_dir.IsEmpty() )
259 curr_dir += wxFileName::GetPathSeparator();
277 wxString curr_dir = item_data->GetDir();
279 if( curr_dir.IsEmpty() )
282 wxString new_dir = wxGetTextFromUser(
_(
"Directory name:" ),
_(
"Create New Directory" ) );
284 if( new_dir.IsEmpty() )
287 wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
289 if( !wxMkdir( full_dirname ) )
319 case TREE_FILE_TYPE::DRILL_NC:
return "nc";
320 case TREE_FILE_TYPE::DRILL_XNC:
return "xnc";
329 case TREE_FILE_TYPE::ROOT:
330 case TREE_FILE_TYPE::UNKNOWN:
331 case TREE_FILE_TYPE::MAX:
332 case TREE_FILE_TYPE::DIRECTORY:
break;
335 return wxEmptyString;
341 std::vector<wxString> projects;
342 wxString dir_filename;
343 bool haveFile = dir.GetFirst( &dir_filename );
347 wxFileName file( dir_filename );
350 projects.push_back( file.GetName() );
352 haveFile = dir.GetNext( &dir_filename );
360 git_repository* repo =
nullptr;
364 if( git_repository_discover( &repo_path, filename, 0, NULL ) )
367 printf(
"get_git_repository_for_file: %s\n", git_error_last()->message ); fflush( 0 );
372 if( git_repository_open( &repo, repo_path.ptr ) )
374 git_buf_free( &repo_path );
379 git_buf_free( &repo_path );
386 const wxTreeItemId& aParent,
387 std::vector<wxString>* aProjectNames,
391 wxFileName fn( aName );
396 if( fn.GetName().StartsWith( wxT(
"." ) ) )
397 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 reg.Compile( wxString::FromAscii(
"^.*\\." ) + ext + wxString::FromAscii(
"$" ),
436 if( reg.Matches( aName ) )
444 wxString file = wxFileNameFromPath( aName );
445 wxFileName currfile( file );
449 if( ( type == TREE_FILE_TYPE::LEGACY_PROJECT )
450 && ( currfile.GetName().CmpNoCase(
project.GetName() ) == 0 ) )
452 return wxTreeItemId();
455 if( currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::LEGACY_SCHEMATIC )
456 || currfile.GetExt() ==
GetFileExt( TREE_FILE_TYPE::SEXPR_SCHEMATIC ) )
461 return wxTreeItemId();
466 wxDir parentDir( parentTreeItem->
GetDir() );
467 std::vector<wxString> projects =
getProjects( parentDir );
470 return wxTreeItemId();
475 wxTreeItemIdValue cookie;
476 wxTreeItemId kid =
m_TreeProject->GetFirstChild( aParent, cookie );
483 return itemData->GetId();
489 if( type == TREE_FILE_TYPE::LEGACY_PROJECT || type == TREE_FILE_TYPE::JSON_PROJECT
490 || type == TREE_FILE_TYPE::LEGACY_SCHEMATIC || type == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
502 if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
506 case TREE_FILE_TYPE::LEGACY_PROJECT:
507 if( itemData->
GetType() == TREE_FILE_TYPE::JSON_PROJECT )
508 return wxTreeItemId();
512 case TREE_FILE_TYPE::LEGACY_SCHEMATIC:
513 if( itemData->
GetType() == TREE_FILE_TYPE::SEXPR_SCHEMATIC )
514 return wxTreeItemId();
518 case TREE_FILE_TYPE::JSON_PROJECT:
519 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_PROJECT )
524 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
525 if( itemData->
GetType() == TREE_FILE_TYPE::LEGACY_SCHEMATIC )
541 wxTreeItemId newItemId =
m_TreeProject->AppendItem( aParent, file );
548 wxString fileName = currfile.GetName().Lower();
549 wxString projName =
project.GetName().Lower();
551 if( fileName == projName || fileName.StartsWith( projName +
"-" ) )
555 bool subdir_populated =
false;
560 if( TREE_FILE_TYPE::DIRECTORY == type && aRecurse )
566 std::vector<wxString> projects =
getProjects( dir );
567 wxString dir_filename;
568 bool haveFile = dir.GetFirst( &dir_filename );
573 subdir_populated = aRecurse;
579 wxString
path = aName + wxFileName::GetPathSeparator() + dir_filename;
582 haveFile = dir.GetNext( &dir_filename );
591 if( subdir_populated )
617 wxFileName fn = pro_dir;
618 bool prjReset =
false;
629 bool prjOpened = fn.FileExists();
639 if( conn_type ==
"https" )
641 else if( conn_type ==
"ssh" )
648 if( !prjOpened && !prjReset )
651 prjOpened = fn.FileExists();
658 m_root =
m_TreeProject->AddRoot( fn.GetFullName(),
static_cast<int>( TREE_FILE_TYPE::ROOT ),
659 static_cast<int>( TREE_FILE_TYPE::ROOT ) );
672 wxDir dir( pro_dir );
676 std::vector<wxString> projects =
getProjects( dir );
678 bool haveFile = dir.GetFirst( &filename );
682 if( filename != fn.GetFullName() )
684 wxString
name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
690 haveFile = dir.GetNext( &filename );
714 git_status_options opts = GIT_STATUS_OPTIONS_INIT;
715 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
716 opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX
717 | GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
719 git_status_list* status_list =
nullptr;
720 int error = git_status_list_new( &status_list, repo, &opts );
722 if( error != GIT_OK )
725 bool has_changed_files = git_status_list_entrycount( status_list ) > 0;
726 git_status_list_free( status_list );
727 return has_changed_files;
733 wxTreeItemId curr_item = Event.GetItem();
740 bool can_switch_to_project =
true;
741 bool can_create_new_directory =
true;
742 bool can_open_this_directory =
true;
743 bool can_edit =
true;
744 bool can_rename =
true;
745 bool can_delete =
true;
749 bool vcs_can_init = !vcs_has_repo;
750 bool vcs_can_remove = vcs_has_repo;
753 bool vcs_can_pull = vcs_can_fetch;
754 bool vcs_can_switch = vcs_has_repo;
758 int major, minor, rev;
759 bool libgit_init = ( git_libgit2_version( &major, &minor, &rev ) == GIT_OK );
761 vcs_menu &= libgit_init;
763 if( selection.size() == 0 )
767 if( selection.size() != 1 )
769 can_switch_to_project =
false;
770 can_create_new_directory =
false;
779 can_switch_to_project =
false;
785 can_delete = item->CanDelete();
786 can_rename = item->CanRename();
788 switch( item->GetType() )
790 case TREE_FILE_TYPE::JSON_PROJECT:
791 case TREE_FILE_TYPE::LEGACY_PROJECT:
796 can_switch_to_project =
false;
800 can_create_new_directory =
false;
801 can_open_this_directory =
false;
805 case TREE_FILE_TYPE::DIRECTORY:
806 can_switch_to_project =
false;
810 case TREE_FILE_TYPE::SEXPR_SCHEMATIC:
811 case TREE_FILE_TYPE::SEXPR_PCB:
815 can_switch_to_project =
false;
816 can_create_new_directory =
false;
817 can_open_this_directory =
false;
827 if( can_switch_to_project )
830 _(
"Close all editors, and switch to the selected project" ),
831 KiBitmap( BITMAPS::open_project ) );
832 popup_menu.AppendSeparator();
835 if( can_create_new_directory )
838 _(
"Create a New Directory" ),
KiBitmap( BITMAPS::directory ) );
841 if( can_open_this_directory )
843 if( selection.size() == 1 )
846 text =
_(
"Reveal in Finder" );
847 help_text =
_(
"Reveals the directory in a Finder window" );
849 text =
_(
"Open Directory in File Explorer" );
850 help_text =
_(
"Opens the directory in the default system file manager" );
856 text =
_(
"Reveal in Finder" );
857 help_text =
_(
"Reveals the directories in a Finder window" );
859 text =
_(
"Open Directories in File Explorer" );
860 help_text =
_(
"Opens the directories in the default system file manager" );
865 KiBitmap( BITMAPS::directory_browser ) );
870 if( selection.size() == 1 )
871 help_text =
_(
"Open the file in a Text Editor" );
873 help_text =
_(
"Open files in a Text Editor" );
881 if( selection.size() == 1 )
883 text =
_(
"Rename File..." );
884 help_text =
_(
"Rename file" );
888 text =
_(
"Rename Files..." );
889 help_text =
_(
"Rename files" );
898 if( selection.size() == 1 )
899 help_text =
_(
"Delete the file and its content" );
901 help_text =
_(
"Delete the files and their contents" );
903 if( can_switch_to_project
904 || can_create_new_directory
905 || can_open_this_directory
909 popup_menu.AppendSeparator();
923 wxMenu* vcs_submenu =
new wxMenu();
924 wxMenu* branch_submenu =
new wxMenu();
925 wxMenuItem* vcs_menuitem =
nullptr;
928 _(
"Add Project to Version Control..." ),
929 _(
"Initialize a new repository" ) );
930 vcs_menuitem->Enable( vcs_can_init );
934 _(
"Commit changes to the local repository" ) );
935 vcs_menuitem->Enable( vcs_can_commit );
937 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PUSH,
_(
"Push" ),
938 _(
"Push committed local changes to remote repository" ) );
939 vcs_menuitem->Enable( vcs_can_push );
941 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PULL,
_(
"Pull" ),
942 _(
"Pull changes from remote repository into local" ) );
943 vcs_menuitem->Enable( vcs_can_pull );
945 vcs_submenu->AppendSeparator();
948 _(
"Commit changes to the local repository" ) );
949 vcs_menuitem->Enable( vcs_can_commit );
951 vcs_submenu->AppendSeparator();
960 for(
size_t ii = 1; ii < branchNames.size() && ii < 6; ++ii )
962 wxString msg =
_(
"Switch to branch " ) + branchNames[ii];
964 vcs_menuitem->Enable( vcs_can_switch );
968 _(
"Switch to a different branch" ) );
969 vcs_menuitem->Enable( vcs_can_switch );
973 vcs_submenu->AppendSeparator();
976 _(
"Delete all version control files from the project directory." ) );
977 vcs_menuitem->Enable( vcs_can_remove );
979 popup_menu.AppendSeparator();
980 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
983 if( popup_menu.GetMenuItemCount() > 0 )
984 PopupMenu( &popup_menu );
990 wxString editorname =
Pgm().GetTextEditor();
992 if( editorname.IsEmpty() )
994 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1003 wxString fullFileName = item_data->GetFileName();
1005 if( !files.IsEmpty() )
1008 files += fullFileName;
1020 item_data->Delete();
1030 if( tree_data.size() != 1 )
1034 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1035 tree_data[0]->GetFileName() );
1036 wxTextEntryDialog dlg(
this, msg,
_(
"Change filename" ), buffer );
1038 if( dlg.ShowModal() != wxID_OK )
1041 buffer = dlg.GetValue();
1042 buffer.Trim(
true );
1043 buffer.Trim(
false );
1045 if( buffer.IsEmpty() )
1048 tree_data[0]->Rename( buffer,
true );
1057 if( tree_data.size() != 1 )
1094 wxTreeItemId itemId = Event.GetItem();
1100 if( tree_data->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1104 wxTreeItemIdValue cookie;
1105 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1108 bool subdir_populated =
false;
1111 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1115 if( !itemData || itemData->
GetType() != TREE_FILE_TYPE::DIRECTORY )
1122 wxDir dir( fileName );
1124 if( dir.IsOpened() )
1126 std::vector<wxString> projects =
getProjects( dir );
1127 wxString dir_filename;
1128 bool haveFile = dir.GetFirst( &dir_filename );
1133 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1136 haveFile = dir.GetNext( &dir_filename );
1142 subdir_populated =
true;
1151 if( subdir_populated )
1159 wxArrayTreeItemIds selection;
1160 std::vector<PROJECT_TREE_ITEM*> data;
1164 for(
auto it = selection.begin(); it != selection.end(); it++ )
1169 wxLogDebug(
"Null tree item returned for selection, dynamic_cast failed?" );
1173 data.push_back( item );
1192 if( prj_dir == aSubDir )
1196 wxTreeItemIdValue cookie;
1197 wxTreeItemId root_id =
m_root;
1198 std::stack<wxTreeItemId> subdirs_id;
1200 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1204 if( ! child.IsOk() )
1206 if( subdirs_id.empty() )
1213 root_id = subdirs_id.top();
1224 if( itemData && ( itemData->
GetType() == TREE_FILE_TYPE::DIRECTORY ) )
1234 subdirs_id.push( child );
1250 const wxFileName& pathModified =
event.GetPath();
1251 wxString subdir = pathModified.GetPath();
1252 wxString fn = pathModified.GetFullPath();
1254 switch( event.GetChangeType() )
1256 case wxFSW_EVENT_DELETE:
1257 case wxFSW_EVENT_CREATE:
1258 case wxFSW_EVENT_RENAME:
1262 case wxFSW_EVENT_MODIFY:
1265 case wxFSW_EVENT_ACCESS:
1272 if( !root_id.IsOk() )
1275 wxTreeItemIdValue cookie;
1276 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1278 switch( event.GetChangeType() )
1280 case wxFSW_EVENT_CREATE:
1282 wxTreeItemId newitem =
1296 case wxFSW_EVENT_DELETE:
1310 case wxFSW_EVENT_RENAME :
1312 const wxFileName& newpath =
event.GetNewPath();
1313 wxString newdir = newpath.GetPath();
1314 wxString newfn = newpath.GetFullPath();
1338 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1344 if( newitem.IsOk() )
1364#if defined( _WIN32 )
1371 m_Parent->SetStatusText(
_(
"Network path: not monitoring folder changes" ), 1 );
1376 m_Parent->SetStatusText(
_(
"Local path: monitoring folder changes" ), 1 );
1393 fn.AssignDir( prj_dir );
1394 fn.DontFollowLink();
1411 wxTreeItemIdValue cookie;
1412 wxTreeItemId root_id =
m_root;
1414 std::stack < wxTreeItemId > subdirs_id;
1416 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1422 if( subdirs_id.empty() )
1428 root_id = subdirs_id.top();
1439 if( itemData && itemData->
GetType() == TREE_FILE_TYPE::DIRECTORY )
1446 if( wxFileName::IsDirReadable(
path ) )
1448 fn.AssignDir(
path );
1453 subdirs_id.push( kid );
1461#if defined(DEBUG) && 1
1462 wxArrayString paths;
1466 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1493 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1494 wxPaintDC dc(
this );
1496 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1497 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1499 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1500 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1514 wxString dir = tree_data->
GetDir();
1518 wxLogError(
"Failed to initialize git project: project directory is empty." );
1523 git_repository* repo =
nullptr;
1524 int error = git_repository_open(&repo, dir.mb_str());
1531 git_repository_free( repo );
1537 error = git_repository_init( &repo, dir.mb_str(), 0 );
1541 git_repository_free( repo );
1547 git_error_last()->message );
1561 dlg.SetTitle(
_(
"Set default remote" ) );
1563 if( dlg.ShowModal() != wxID_OK )
1573 git_remote* remote =
nullptr;
1582 fullURL = dlg.
GetRepoURL().StartsWith(
"https" ) ?
"https://" :
"http://";
1590 fullURL.append( wxS(
":" ) );
1594 fullURL.append( wxS(
"@" ) );
1605 error = git_remote_create_with_fetchspec( &remote, repo,
"origin",
1606 fullURL.ToStdString().c_str(),
1607 "+refs/heads/*:refs/remotes/origin/*" );
1609 if( error != GIT_OK )
1615 git_error_last()->message );
1630 handler.
SetProgressReporter( std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetching Remote" ), 1 ) );
1667 handler.
SetProgressReporter( std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetching Remote" ), 1 ) );
1687 handler.
SetProgressReporter( std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetching Remote" ), 1 ) );
1689 if( handler.
PerformPush() != PushResult::Success )
1702 if(
int error = git_reference_name_to_id( &head_oid, aRepo,
"HEAD" ) != 0 )
1704 wxLogError(
"Failed to lookup HEAD reference" );
1709 git_commit* commit =
nullptr;
1710 if(
int error = git_commit_lookup( &commit, aRepo, &head_oid ) != GIT_OK )
1712 wxLogError(
"Failed to lookup commit" );
1716 git_reference* branchRef =
nullptr;
1718 if( git_branch_create( &branchRef, aRepo, aBranchName.mb_str(), commit, 0 ) != 0 )
1720 wxLogError(
"Failed to create branch" );
1721 git_commit_free( commit );
1725 git_commit_free( commit );
1726 git_reference_free( branchRef );
1741 int retval = dlg.ShowModal();
1744 if( retval == wxID_ADD )
1746 else if( retval != wxID_OK )
1750 git_reference* branchRef =
nullptr;
1752 if( git_reference_lookup( &branchRef, repo, branchName.mb_str() ) != GIT_OK &&
1753 git_reference_dwim( &branchRef, repo, branchName.mb_str() ) != GIT_OK )
1755 wxString errorMessage = wxString::Format(
_(
"Failed to lookup branch '%s': %s" ), branchName, giterr_last()->message );
1760 const char* branchRefName = git_reference_name( branchRef );
1762 git_object* branchObj =
nullptr;
1764 if( git_revparse_single( &branchObj, repo, branchName.mb_str() ) != 0 )
1766 wxString errorMessage =
1767 wxString::Format(
_(
"Failed to find branch head for '%s'" ), branchName );
1769 git_reference_free( branchRef );
1775 if( git_checkout_tree( repo, branchObj,
nullptr ) != 0 )
1777 wxString errorMessage =
1778 wxString::Format(
_(
"Failed to switch to branch '%s'" ), branchName );
1780 git_reference_free( branchRef );
1781 git_object_free( branchObj );
1786 if( git_repository_set_head( repo, branchRefName ) != 0 )
1788 wxString errorMessage = wxString::Format(
1789 _(
"Failed to update HEAD reference for branch '%s'" ), branchName );
1791 git_reference_free( branchRef );
1792 git_object_free( branchObj );
1797 git_reference_free( branchRef );
1798 git_object_free( branchObj );
1807 || !
IsOK(
this,
_(
"Are you sure you want to remove git tracking from this project?" ) ) )
1813 git_repository_free( repo );
1818 fn.AppendDir(
".git" );
1828 std::stack<wxTreeItemId> items;
1831 while( !items.empty() )
1833 wxTreeItemId current = items.top();
1837 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
1839 wxTreeItemIdValue cookie;
1840 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
1842 while( child.IsOk() )
1844 items.push( child );
1861 if( timeSinceLastUpdate.Abs() < wxTimeSpan::Seconds( 2 ) )
1878 git_reference* currentBranchReference =
nullptr;
1879 git_repository_head( ¤tBranchReference, repo );
1882 if( currentBranchReference )
1885 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
1886 wxString branchName = git_reference_shorthand( currentBranchReference );
1888 m_TreeProject->SetItemText( kid, filename +
" [" + branchName +
"]" );
1889 git_reference_free( currentBranchReference );
1894 wxLogError(
"Failed to lookup current branch: %s", giterr_last()->message );
1900 std::map<wxString, wxTreeItemId> branchMap;
1902 std::stack<wxTreeItemId> items;
1905 while( !items.empty() )
1914 wxTreeItemIdValue cookie;
1915 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
1917 while( child.IsOk() )
1919 items.push( child );
1925 git_status_options status_options = GIT_STATUS_OPTIONS_INIT;
1926 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
1927 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
1929 git_index* index =
nullptr;
1931 if( git_repository_index( &index, repo ) != GIT_OK )
1934 wxLogDebug(
"Failed to get git index: %s", giterr_last()->message );
1938 git_status_list* status_list =
nullptr;
1940 if( git_status_list_new( &status_list, repo, &status_options ) != GIT_OK )
1942 wxLogDebug(
"Failed to get git status list: %s", giterr_last()->message );
1943 git_index_free( index );
1949 size_t count = git_status_list_entrycount( status_list );
1951 for(
size_t ii = 0; ii < count; ++ii )
1953 const git_status_entry* entry = git_status_byindex( status_list, ii );
1954 std::string
path( entry->head_to_index? entry->head_to_index->old_file.path
1955 : entry->index_to_workdir->old_file.path );
1956 wxFileName fn(
path );
1957 fn.MakeAbsolute( git_repository_workdir( repo ) );
1959 auto iter = branchMap.find( fn.GetFullPath() );
1961 if( iter == branchMap.end() )
1967 if( entry->status == GIT_STATUS_CURRENT )
1973 else if( entry->status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
1980 else if( entry->status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
1987 else if( entry->status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
1996 if( localChanges.count(
path ) )
2003 else if( remoteChanges.count(
path ) )
2019 git_status_list_free( status_list );
2020 git_index_free( index );
2030 if( repo ==
nullptr )
2032 wxMessageBox(
"The selected directory is not a git project." );
2036 git_config*
config =
nullptr;
2037 git_repository_config( &
config, repo );
2040 wxString authorName;
2041 wxString authorEmail;
2044 git_config_entry* name_c =
nullptr;
2045 git_config_entry* email_c =
nullptr;
2046 int authorNameError = git_config_get_entry( &name_c,
config,
"user.name" );
2048 if( authorNameError != 0 || name_c ==
nullptr )
2050 authorName =
Pgm().GetCommonSettings()->m_Git.authorName;
2054 authorName = name_c->value;
2055 git_config_entry_free( name_c );
2059 int authorEmailError = git_config_get_entry( &email_c,
config,
"user.email" );
2061 if( authorEmailError != 0 || email_c ==
nullptr )
2063 authorEmail =
Pgm().GetCommonSettings()->m_Git.authorEmail;
2067 authorEmail = email_c->value;
2068 git_config_entry_free( email_c );
2072 git_config_free(
config );
2075 git_status_options status_options = GIT_STATUS_OPTIONS_INIT;
2076 status_options.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
2077 status_options.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
2079 git_status_list* status_list =
nullptr;
2080 git_status_list_new( &status_list, repo, &status_options );
2082 std::map<wxString, int> modifiedFiles;
2084 size_t count = git_status_list_entrycount( status_list );
2086 std::set<wxString> selected_files;
2090 if( item->GetType() != TREE_FILE_TYPE::DIRECTORY )
2091 selected_files.emplace( item->GetFileName() );
2094 for(
size_t i = 0; i < count; ++i )
2096 const git_status_entry* entry = git_status_byindex( status_list, i );
2099 if( entry->status == GIT_STATUS_CURRENT
2100 || ( entry->status & ( GIT_STATUS_CONFLICTED | GIT_STATUS_IGNORED ) ) )
2105 wxFileName fn( entry->index_to_workdir->old_file.path );
2106 fn.MakeAbsolute( git_repository_workdir( repo ) );
2108 wxString filePath( entry->index_to_workdir->old_file.path, wxConvUTF8 );
2112 modifiedFiles.emplace( filePath, entry->status );
2114 else if( selected_files.count( fn.GetFullPath() ) )
2116 modifiedFiles.emplace( filePath, entry->status );
2120 git_status_list_free( status_list );
2124 auto ret = dlg.ShowModal();
2126 if( ret == wxID_OK )
2130 git_tree* tree =
nullptr;
2131 git_commit* parent =
nullptr;
2132 git_index* index =
nullptr;
2138 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2144 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2148 if( git_repository_index( &index, repo ) != 0 )
2150 wxMessageBox(
_(
"Failed to get repository index: %s" ), giterr_last()->message );
2154 for( wxString& file :files )
2156 if( git_index_add_bypath( index, file.mb_str() ) != 0 )
2158 wxMessageBox(
_(
"Failed to add file to index: %s" ), giterr_last()->message );
2159 git_index_free( index );
2164 if( git_index_write( index ) != 0 )
2166 wxMessageBox(
_(
"Failed to write index: %s" ), giterr_last()->message );
2167 git_index_free( index );
2171 if (git_index_write_tree( &tree_id, index ) != 0)
2173 wxMessageBox(
_(
"Failed to write tree: %s" ), giterr_last()->message );
2174 git_index_free( index );
2178 git_index_free( index );
2180 if( git_tree_lookup( &tree, repo, &tree_id ) != 0 )
2182 wxMessageBox(
_(
"Failed to lookup tree: %s" ), giterr_last()->message );
2186 git_reference* headRef =
nullptr;
2188 if( git_repository_head( &headRef, repo ) != 0 )
2190 wxMessageBox(
_(
"Failed to get HEAD reference: %s" ), giterr_last()->message );
2191 git_index_free( index );
2195 if( git_reference_peel( (git_object**) &parent, headRef, GIT_OBJECT_COMMIT ) != 0 )
2197 wxMessageBox(
_(
"Failed to get commit: %s" ), giterr_last()->message );
2198 git_reference_free( headRef );
2199 git_index_free( index );
2203 git_reference_free( headRef );
2209 git_signature* author =
nullptr;
2211 if( git_signature_now( &author, author_name.mb_str(), author_email.mb_str() ) != 0 )
2213 wxMessageBox(
_(
"Failed to create author signature: %s" ), giterr_last()->message );
2218 const git_commit* parents[1] = { parent };
2220 if( git_commit_create( &oid, repo,
"HEAD", author, author,
nullptr, commit_msg.mb_str(), tree,
2223 wxMessageBox(
_(
"Failed to create commit: %s" ), giterr_last()->message );
2227 git_signature_free( author );
2228 git_commit_free( parent );
2229 git_tree_free( tree );
2249 if( git_repository_index( &index, repo ) != 0 )
2253 if( git_index_find( &entry_pos, index, aFile.mb_str() ) == 0 )
2255 git_index_free( index );
2259 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.
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()
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
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.
bool RmDirRecursive(const wxString &aFileName, wxString *aErrors)
Removes the directory aDirName and all its contents including subdirectories and their files.
int ExecuteFile(const wxString &aEditorName, const wxString &aFileName, wxProcess *aCallback)
Call the executable file aEditorName with the parameter aFileName.
bool m_EnableGit
When true, enable git integration.
const std::string SpiceFileExtension
const std::string LegacyPcbFileExtension
const std::string PdfFileExtension
const std::string KiCadSymbolLibFileExtension
const std::string LegacySchematicFileExtension
const std::string FootprintAssignmentFileExtension
const std::string FootprintPlaceFileExtension
const std::string GerberJobFileExtension
const std::string DrillFileExtension
const wxString GerberFileExtensionsRegex
const std::string KiCadFootprintFileExtension
const std::string LegacyProjectFileExtension
const std::string MarkdownFileExtension
const std::string KiCadPcbFileExtension
const std::string SVGFileExtension
const std::string TextFileExtension
const std::string ProjectFileExtension
const std::string NetlistFileExtension
const std::string DesignRulesFileExtension
const std::string HtmlFileExtension
const std::string KiCadSchematicFileExtension
const std::string ReportFileExtension
const std::string DrawingSheetFileExtension
const std::string ArchiveFileExtension
const std::string LegacySymbolLibFileExtension
const wxChar *const tracePathsAndFiles
Flag to enable path and file name debug 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 ...
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &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.
#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)
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
#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.