30#include <wx/stdpaths.h>
33#include <wx/textdlg.h>
35#include <wx/wupdlock.h>
58#include <wx/dcclient.h>
59#include <wx/progdlg.h>
60#include <wx/settings.h>
109 wxT(
"^.*\\.kicad_pro$" ),
112 wxT(
"^.*\\.kicad_sch$" ),
113 wxT(
"^[^$].*\\.brd$" ),
114 wxT(
"^[^$].*\\.kicad_pcb$" ),
115 wxT(
"^[^$].*\\.kicad_dru$" ),
116 wxT(
"^[^$].*\\.kicad_wks$" ),
117 wxT(
"^[^$].*\\.kicad_mod$" ),
121 wxT(
"^.*\\.kicad_sym$" ),
126 wxT(
"^.*\\.gbrjob$" ),
127 wxT(
"^.*\\.gb[alops]$" ),
128 wxT(
"^.*\\.gt[alops]$" ),
129 wxT(
"^.*\\.g[0-9]{1,2}$" ),
130 wxT(
"^.*\\.gm[0-9]{1,2}$" ),
134 wxT(
"^.*\\.html$" ),
145 wxT(
"^.*\\.kicad_jobset" ),
228 wxSashLayoutWindow( parent,
ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxTAB_TRAVERSAL )
239 Bind( wxEVT_FSWATCHER,
242 Bind( wxEVT_SYS_COLOUR_CHANGED,
256 m_filters.emplace_back( wxT(
"^no KiCad files found" ) );
264 Unbind( wxEVT_FSWATCHER,
266 Unbind( wxEVT_SYS_COLOUR_CHANGED,
299 if( tree_data.size() != 1 )
302 wxString prj_filename = tree_data[0]->GetFileName();
304 m_Parent->LoadProject( prj_filename );
316 wxString curr_dir = item_data->GetDir();
318 if( curr_dir.IsEmpty() )
321 curr_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
324 if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
327 if( !curr_dir.IsEmpty() )
328 curr_dir += wxFileName::GetPathSeparator();
343 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
346 wxString curr_dir = item_data->GetDir();
348 if( curr_dir.IsEmpty() )
351 wxString new_dir = wxGetTextFromUser(
_(
"Directory name:" ),
_(
"Create New Directory" ) );
353 if( new_dir.IsEmpty() )
356 wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
358 if( !wxMkdir( full_dirname ) )
406 return wxEmptyString;
412 std::vector<wxString> projects;
413 wxString dir_filename;
414 bool haveFile = dir.GetFirst( &dir_filename );
418 wxFileName file( dir_filename );
422 projects.push_back( file.GetName() );
424 haveFile = dir.GetNext( &dir_filename );
435 const wxTreeItemId& aParent,
436 std::vector<wxString>* aProjectNames,
440 wxFileName fn( aName );
443 return wxTreeItemId();
445 if( wxDirExists( aName ) )
453 bool addFile =
false;
455 for(
const wxString& m_filter :
m_filters )
457 wxCHECK2_MSG( reg.Compile( m_filter, wxRE_ICASE ),
continue,
458 wxString::Format(
"Regex %s failed to compile.", m_filter ) );
460 if( reg.Matches( aName ) )
468 return wxTreeItemId();
475 if( ext == wxT(
"" ) )
478 if( reg.Compile( wxString::FromAscii(
"^.*\\." ) + ext + wxString::FromAscii(
"$" ), wxRE_ICASE )
479 && reg.Matches( aName ) )
487 wxString file = wxFileNameFromPath( aName );
488 wxFileName currfile( file );
490 bool showAllSchematics =
m_TreeProject->GetGitRepo() !=
nullptr;
494 && ( currfile.GetName().CmpNoCase(
project.GetName() ) == 0 ) )
496 return wxTreeItemId();
505 return wxTreeItemId();
511 if( !parentTreeItem )
512 return wxTreeItemId();
514 wxDir parentDir( parentTreeItem->
GetDir() );
515 std::vector<wxString> projects =
getProjects( parentDir );
518 return wxTreeItemId();
523 wxTreeItemIdValue cookie;
524 wxTreeItemId kid =
m_TreeProject->GetFirstChild( aParent, cookie );
531 return itemData->GetId();
552 if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
558 return wxTreeItemId();
564 return wxTreeItemId();
591 wxTreeItemId newItemId =
m_TreeProject->AppendItem( aParent, file );
598 wxString fileName = currfile.GetName().Lower();
599 wxString projName =
project.GetName().Lower();
601 if( fileName == projName || fileName.StartsWith( projName +
"-" ) )
605 bool subdir_populated =
false;
616 std::vector<wxString> projects =
getProjects( dir );
617 wxString dir_filename;
618 bool haveFile = dir.GetFirst( &dir_filename );
623 subdir_populated = aRecurse;
629 wxString
path = aName + wxFileName::GetPathSeparator() + dir_filename;
632 haveFile = dir.GetNext( &dir_filename );
641 if( subdir_populated )
663 wxString pro_dir =
m_Parent->GetProjectFileName();
681 wxFileName fn = pro_dir;
682 bool prjReset =
false;
693 bool prjOpened = fn.FileExists();
696 if(
Pgm().GetCommonSettings()->m_Git.enableGit
697 && !
Prj().GetLocalSettings().m_GitIntegrationDisabled )
707 const char* canonicalWorkDir = git_repository_workdir(
m_TreeProject->GetGitRepo() );
709 if( canonicalWorkDir )
712 fn.GetPath(), wxString::FromUTF8( canonicalWorkDir ) );
716 m_TreeProject->GitCommon()->SetUsername(
Prj().GetLocalSettings().m_GitRepoUsername );
717 m_TreeProject->GitCommon()->SetSSHKey(
Prj().GetLocalSettings().m_GitSSHKey );
724 if( !prjOpened && !prjReset )
727 prjOpened = fn.FileExists();
747 pro_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
748 wxDir dir( pro_dir );
752 std::vector<wxString> projects =
getProjects( dir );
754 bool haveFile = dir.GetFirst( &filename );
758 if( filename != fn.GetFullName() )
760 wxString
name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
767 haveFile = dir.GetNext( &filename );
784 wxLogTrace(
traceGit,
"PROJECT_TREE_PANE::ReCreateTreePrj: starting timers" );
803 wxTreeItemId curr_item = Event.GetItem();
811 bool can_switch_to_project =
true;
812 bool can_create_new_directory =
true;
813 bool can_open_this_directory =
true;
814 bool can_edit =
true;
815 bool can_rename =
true;
816 bool can_delete =
true;
817 bool run_jobs =
false;
821 bool vcs_can_init = !vcs_has_repo;
825 bool vcs_can_remove = vcs_has_repo || gitIntegrationDisabled;
828 bool vcs_can_pull = vcs_can_fetch;
829 bool vcs_can_switch = vcs_has_repo;
835 vcs_menu &= libgit_init;
837 if( selection.size() == 0 )
841 if( selection.size() != 1 )
843 can_switch_to_project =
false;
844 can_create_new_directory =
false;
853 can_switch_to_project =
false;
859 can_delete = item->CanDelete();
860 can_rename = item->CanRename();
862 switch( item->GetType() )
870 can_switch_to_project =
false;
874 can_create_new_directory =
false;
875 can_open_this_directory =
false;
880 can_switch_to_project =
false;
887 can_switch_to_project =
false;
888 can_create_new_directory =
false;
889 can_open_this_directory =
false;
902 can_switch_to_project =
false;
903 can_create_new_directory =
false;
904 can_open_this_directory =
false;
914 if( can_switch_to_project )
917 _(
"Close all editors, and switch to the selected project" ),
919 popup_menu.AppendSeparator();
922 if( can_create_new_directory )
928 if( can_open_this_directory )
930 if( selection.size() == 1 )
933 text =
_(
"Reveal in Finder" );
934 help_text =
_(
"Reveals the directory in a Finder window" );
936 text =
_(
"Open Directory in File Explorer" );
937 help_text =
_(
"Opens the directory in the default system file manager" );
943 text =
_(
"Reveal in Finder" );
944 help_text =
_(
"Reveals the directories in a Finder window" );
946 text =
_(
"Open Directories in File Explorer" );
947 help_text =
_(
"Opens the directories in the default system file manager" );
957 if( selection.size() == 1 )
958 help_text =
_(
"Open the file in a Text Editor" );
960 help_text =
_(
"Open files in a Text Editor" );
966 if( run_jobs && selection.size() == 1 )
974 if( selection.size() == 1 )
976 text =
_(
"Rename File..." );
977 help_text =
_(
"Rename file" );
981 text =
_(
"Rename Files..." );
982 help_text =
_(
"Rename files" );
991 if( selection.size() == 1 )
992 help_text =
_(
"Delete the file and its content" );
994 help_text =
_(
"Delete the files and their contents" );
996 if( can_switch_to_project
997 || can_create_new_directory
998 || can_open_this_directory
1002 popup_menu.AppendSeparator();
1016 wxMenu* vcs_submenu =
new wxMenu();
1017 wxMenu* branch_submenu =
new wxMenu();
1018 wxMenuItem* vcs_menuitem =
nullptr;
1021 _(
"Add Project to Version Control..." ),
1022 _(
"Initialize a new repository" ) );
1023 vcs_menuitem->Enable( vcs_can_init );
1027 _(
"Commit changes to the local repository" ) );
1028 vcs_menuitem->Enable( vcs_can_commit );
1030 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PUSH,
_(
"Push" ),
1031 _(
"Push committed local changes to remote repository" ) );
1032 vcs_menuitem->Enable( vcs_can_push );
1034 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PULL,
_(
"Pull" ),
1035 _(
"Pull changes from remote repository into local" ) );
1036 vcs_menuitem->Enable( vcs_can_pull );
1038 vcs_submenu->AppendSeparator();
1041 _(
"Commit changes to the local repository" ) );
1042 vcs_menuitem->Enable( vcs_can_commit );
1044 vcs_submenu->AppendSeparator();
1050 std::vector<wxString> branchNames =
m_TreeProject->GitCommon()->GetBranchNames();
1053 for(
size_t ii = 1; ii < branchNames.size() && ii < 6; ++ii )
1055 wxString msg =
_(
"Switch to branch " ) + branchNames[ii];
1057 vcs_menuitem->Enable( vcs_can_switch );
1061 _(
"Switch to a different branch" ) );
1062 vcs_menuitem->Enable( vcs_can_switch );
1066 vcs_submenu->AppendSeparator();
1068 if( gitIntegrationDisabled )
1070 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Enable Git Integration" ),
1071 _(
"Re-enable Git integration for this project" ) );
1075 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Disable Git Integration" ),
1076 _(
"Disable Git integration for this project" ) );
1079 vcs_menuitem->Enable( vcs_can_remove );
1081 popup_menu.AppendSeparator();
1082 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
1085 if( popup_menu.GetMenuItemCount() > 0 )
1086 PopupMenu( &popup_menu );
1094 if( editorname.IsEmpty() )
1096 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1104 wxString fullFileName = item_data->GetFileName();
1106 if( !fullFileName.IsEmpty() )
1108 ExecuteFile( editorname, fullFileName.wc_str(),
nullptr,
false );
1119 item_data->Delete();
1129 if( tree_data.size() != 1 )
1133 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1134 tree_data[0]->GetFileName() );
1135 wxTextEntryDialog dlg( wxGetTopLevelParent(
this ), msg,
_(
"Change filename" ), buffer );
1137 if( dlg.ShowModal() != wxID_OK )
1140 buffer = dlg.GetValue();
1141 buffer.Trim(
true );
1142 buffer.Trim(
false );
1144 if( buffer.IsEmpty() )
1147 tree_data[0]->Rename( buffer,
true );
1156 if( tree_data.size() != 1 )
1179 std::vector<wxTreeItemId> validItemIds;
1182 for( wxTreeItemId
id : validItemIds )
1201 wxTreeItemId itemId = Event.GetItem();
1211 wxTreeItemIdValue cookie;
1212 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1215 bool subdir_populated =
false;
1218 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1229 wxDir dir( fileName );
1231 if( dir.IsOpened() )
1233 std::vector<wxString> projects =
getProjects( dir );
1234 wxString dir_filename;
1235 bool haveFile = dir.GetFirst( &dir_filename );
1240 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1243 haveFile = dir.GetNext( &dir_filename );
1249 subdir_populated =
true;
1258 if( subdir_populated )
1266 wxArrayTreeItemIds selection;
1267 std::vector<PROJECT_TREE_ITEM*> data;
1271 for(
const wxTreeItemId itemId : selection )
1277 wxLogTrace(
traceGit, wxS(
"Null tree item returned for selection, dynamic_cast failed?" ) );
1281 data.push_back( item );
1296 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
1300 if( prj_dir == aSubDir )
1304 wxTreeItemIdValue cookie;
1305 wxTreeItemId root_id =
m_root;
1306 std::stack<wxTreeItemId> subdirs_id;
1308 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1312 if( ! child.IsOk() )
1314 if( subdirs_id.empty() )
1321 root_id = subdirs_id.top();
1342 subdirs_id.push( child );
1360 if( !( event.GetChangeType() & ( wxFSW_EVENT_CREATE |
1361 wxFSW_EVENT_DELETE |
1362 wxFSW_EVENT_RENAME |
1363 wxFSW_EVENT_MODIFY ) ) )
1368 const wxFileName& pathModified =
event.GetPath();
1371 if( pathModified.GetFullPath().Contains( wxS(
".history" ) ) )
1374 wxString subdir = pathModified.GetPath();
1375 wxString fn = pathModified.GetFullPath();
1378 if( pathModified.GetFullName().IsEmpty() )
1380 subdir = subdir.BeforeLast(
'/' );
1381 fn = fn.BeforeLast(
'/' );
1386 if( !root_id.IsOk() )
1389 CallAfter( [
this] ()
1391 wxLogTrace(
traceGit, wxS(
"File system event detected, updating tree cache" ) );
1395 wxTreeItemIdValue cookie;
1396 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1398 switch( event.GetChangeType() )
1400 case wxFSW_EVENT_CREATE:
1415 case wxFSW_EVENT_DELETE:
1429 case wxFSW_EVENT_RENAME :
1431 const wxFileName& newpath =
event.GetNewPath();
1432 wxString newdir = newpath.GetPath();
1433 wxString newfn = newpath.GetFullPath();
1457 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1463 if( newitem.IsOk() )
1484 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
1486#if defined( _WIN32 )
1495 m_Parent->m_FileWatcherInfo =
_(
"Network path: not monitoring folder changes" );
1501 m_Parent->m_FileWatcherInfo =
_(
"Local path: monitoring folder changes" );
1519 class WatcherLogHandler :
public wxLog
1522 explicit WatcherLogHandler(
bool* err ) :
1530 void DoLogTextAtLevel( wxLogLevel level,
const wxString&
text )
override
1532 if( m_err && ( level == wxLOG_Error || level == wxLOG_FatalError ) )
1540 bool watcherHasError =
false;
1541 WatcherLogHandler tmpLog( &watcherHasError );
1542 wxLog* oldLog = wxLog::SetActiveTarget( &tmpLog );
1548 wxLog::SetActiveTarget( oldLog );
1550 if( watcherHasError )
1558 fn.AssignDir( prj_dir );
1559 fn.DontFollowLink();
1573 TO_UTF8( fn.GetFullPath() ) );
1581 TO_UTF8( fn.GetFullPath() ) );
1586 if( m_TreeProject->IsEmpty() )
1590 wxTreeItemIdValue cookie;
1591 wxTreeItemId root_id = m_root;
1593 std::stack < wxTreeItemId > subdirs_id;
1595 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1596 int total_watch_count = 0;
1602 if( subdirs_id.empty() )
1608 root_id = subdirs_id.top();
1610 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1625 if(
path.Contains( wxS(
".history" ) ) )
1627 kid = m_TreeProject->GetNextChild( root_id, cookie );
1633 if( wxFileName::IsDirReadable(
path ) )
1638 fn.AssignDir(
path );
1639 m_watcher->Add( fn );
1640 total_watch_count++;
1644 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1645 subdirs_id.push( kid );
1649 kid = m_TreeProject->GetNextChild( root_id, cookie );
1656#if defined(DEBUG) && 1
1657 wxArrayString paths;
1658 m_watcher->GetWatchedPaths( &paths );
1661 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1679 std::unique_lock<std::mutex> lock( common->
m_gitActionMutex, std::try_to_lock );
1681 constexpr auto kGraceMs = std::chrono::seconds( 2 );
1682 auto graceEnd = std::chrono::steady_clock::now() + kGraceMs;
1684 while( !lock.owns_lock() && std::chrono::steady_clock::now() < graceEnd )
1686 if( lock.try_lock() )
1688 std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
1691 constexpr auto kCheckInterval = std::chrono::seconds( 30 );
1692 bool userAbandoned =
false;
1694 while( !lock.owns_lock() && !userAbandoned )
1696 auto intervalEnd = std::chrono::steady_clock::now() + kCheckInterval;
1699 wxProgressDialog progress(
_(
"Please wait" ),
1700 _(
"Closing project..." ),
1702 wxPD_APP_MODAL | wxPD_SMOOTH );
1704 while( !lock.try_lock()
1705 && std::chrono::steady_clock::now() < intervalEnd )
1708 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
1713 if( lock.owns_lock() )
1716 wxMessageDialog ask(
this,
1717 _(
"A Git operation is still running.\n"
1718 "Keep waiting, or abandon?" ),
1719 _(
"Git Operation Delayed" ),
1720 wxYES_NO | wxICON_QUESTION );
1721 ask.SetYesNoLabels(
_(
"Keep Waiting" ),
_(
"Abandon" ) );
1723 if( ask.ShowModal() == wxID_NO )
1724 userAbandoned =
true;
1730 std::unique_ptr<KIGIT_COMMON> oldCommon =
m_TreeProject->TakeGitCommon();
1740 wxString projectDir = oldCommon ? oldCommon->GetProjectDir() : wxString();
1742 auto cleanup = [orphan, old = std::move( oldCommon )]()
mutable
1744 std::lock_guard<std::mutex> g( old->m_gitActionMutex );
1745 git_repository_free( orphan );
1752 std::string label =
"abandon close " + projectDir.ToStdString();
1789 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1790 wxPaintDC dc(
this );
1792 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1793 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1795 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1796 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1839 wxString dir = tree_data->
GetDir();
1843 wxLogError(
"Failed to initialize git project: project directory is empty." );
1848 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1853 _(
"The selected directory is already a Git project." ) );
1869 const char* canonicalWorkDir = git_repository_workdir( initHandler.
GetRepo() );
1871 if( canonicalWorkDir )
1874 dir, wxString::FromUTF8( canonicalWorkDir ) );
1875 m_TreeProject->GitCommon()->SetProjectDir( symlinkWorkDir );
1879 dlg.SetTitle(
_(
"Set default remote" ) );
1910 std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetch Remote" ), 1,
PR_NO_ABORT ) );
1955 std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetch Remote" ), 1,
PR_NO_ABORT ) );
1986 std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetch Remote" ), 1,
PR_NO_ABORT ) );
2008 wxString branchName;
2017 if( retval == wxID_ADD )
2019 else if( retval != wxID_OK )
2024 std::vector<wxString> branches =
m_TreeProject->GitCommon()->GetBranchNames();
2027 if( branchIndex < 0 ||
static_cast<size_t>( branchIndex ) >= branches.size() )
2030 branchName = branches[branchIndex];
2033 wxLogTrace(
traceGit, wxS(
"onGitSwitchBranch: Switching to branch '%s'" ), branchName );
2048 wxLogTrace(
traceGit, wxS(
"onGitRemoveVCS: Git integration %s" ),
2058 std::stack<wxTreeItemId> items;
2061 while( !items.empty() )
2063 wxTreeItemId current = items.top();
2066 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
2068 wxTreeItemIdValue cookie;
2069 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
2071 while( child.IsOk() )
2073 items.push( child );
2081 wxFileName fn(
Prj().GetProjectPath() );
2088 const char* canonicalWorkDir = git_repository_workdir(
m_TreeProject->GetGitRepo() );
2090 if( canonicalWorkDir )
2093 fn.GetPath(), wxString::FromUTF8( canonicalWorkDir ) );
2094 m_TreeProject->GitCommon()->SetProjectDir( symlinkWorkDir );
2109 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Updating git status icons" ) );
2112 if( !lock.owns_lock() )
2114 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Failed to acquire lock for git status icon update" ) );
2121 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Git is disabled or tree control is null" ) );
2125 std::stack<wxTreeItemId> items;
2128 while( !items.empty() )
2130 wxTreeItemId current = items.top();
2135 wxTreeItemIdValue cookie;
2136 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
2138 while( child.IsOk() )
2140 items.push( child );
2144 m_TreeProject->SetItemState( child,
static_cast<int>( it->second ) );
2159 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
2165 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Git status icons updated" ) );
2171 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Updating tree cache" ) );
2175 if( !lock.owns_lock() )
2177 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Failed to acquire lock for tree cache update" ) );
2183 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Tree control is null" ) );
2194 std::stack<wxTreeItemId> items;
2197 while( !items.empty() )
2209 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2213 wxTreeItemIdValue cookie;
2214 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
2216 while( child.IsOk() )
2218 items.push( child );
2227 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Updating git status icons" ) );
2228#if defined( _WIN32 )
2251 if( !lock1.owns_lock() || !lock2.owns_lock() )
2253 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Failed to acquire locks for git status icon update" ) );
2259 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: No git repository found" ) );
2265 std::unique_lock<std::mutex> gitLock(
m_TreeProject->GitCommon()->m_gitActionMutex, std::try_to_lock );
2267 if( !gitLock.owns_lock() )
2269 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Failed to acquire git action mutex" ) );
2276 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Cancelled" ) );
2283 wxFileName rootFilename(
Prj().GetProjectFullName() );
2286 wxFileName relative = rootFilename;
2287 relative.MakeRelativeTo( repoWorkDir );
2288 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
2291 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
2295 auto fileStatusMap = statusHandler.
GetFileStatus( pathspecStr );
2296 auto [localChanges, remoteChanges] =
m_TreeProject->GitCommon()->GetDifferentFiles();
2299 bool updated =
false;
2302 for(
const auto& [absPath, fileStatus] : fileStatusMap )
2307 wxLogTrace(
traceGit, wxS(
"File '%s' not found in tree cache" ), absPath );
2311 auto [it, inserted] =
m_gitStatusIcons.try_emplace( iter->second, fileStatus.status );
2312 if( inserted || it->second != fileStatus.status )
2314 it->second = fileStatus.status;
2320 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Updated git status icons" ) );
2340 if( repo ==
nullptr )
2342 wxMessageBox(
_(
"The selected directory is not a Git project." ) );
2354 std::map<wxString, int> modifiedFiles;
2355 std::set<wxString> selected_files;
2362 wxString itemPath = item->GetFileName();
2364 itemPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2366 selected_files.emplace( itemPath );
2373 projectPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2376 for(
const auto& [absPath, fileStatus] : fileStatusMap )
2386 wxFileName fn( absPath );
2389 wxString relativePath = absPath;
2390 if( relativePath.StartsWith( repoWorkDir ) )
2392 relativePath = relativePath.Mid( repoWorkDir.length() );
2394 relativePath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2399 if( !absPath.StartsWith( projectPath ) )
2414 if( fn.GetPath().Contains(
Prj().GetProjectName() + wxT(
"-backups" ) ) )
2419 modifiedFiles.emplace( relativePath, fileStatus.gitStatus );
2421 else if( selected_files.count( absPath ) )
2423 modifiedFiles.emplace( relativePath, fileStatus.gitStatus );
2432 if( ret != wxID_OK )
2439 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2445 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2455 wxMessageBox( wxString::Format(
_(
"Failed to create commit: %s" ),
2460 wxLogTrace(
traceGit, wxS(
"Created commit" ) );
2480 for(
const auto& [filePath, fileStatus] : fileStatusMap )
2482 if( filePath.EndsWith( aFile ) || filePath == aFile )
2496 wxLogTrace(
traceGit,
"Syncing project" );
2501 wxLogTrace(
traceGit,
"sync: No git repository found" );
2580 wxLogTrace(
traceGit,
"onGitSyncTimer" );
2594 wxLogTrace(
traceGit,
"onGitSyncTimer: No git repository found" );
2601 wxLogTrace(
traceGit,
"onGitSyncTimer: Cancelled" );
2615 wxLogTrace(
traceGit,
"onGitSyncTimer: Restarting git sync timer" );
2638 wxLogTrace(
traceGit,
"onGitStatusTimer" );
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 GetConnType() const
wxString GetPassword() const
wxString GetUsername() const
wxString GetSSHKey() const
bool SaveCredentials() const
KIGIT_COMMON::GIT_CONN_TYPE GetRepoType() const
wxString GetRepoSSHPath() const
wxString GetRepoURL() const
wxString GetUsername() const
wxString GetPassword() const
void SetSkipButtonLabel(const wxString &aLabel)
wxString GetBranchName() const
virtual bool IsLibraryAvailable()=0
KIGIT_ORPHAN_REGISTRY & OrphanRegistry()
Return the process-wide orphan thread registry owned by this backend.
BranchResult SwitchToBranch(const wxString &aBranchName)
Switch to the specified branch.
CommitResult PerformCommit(const std::vector< wxString > &aFiles, const wxString &aMessage, const wxString &aAuthorName, const wxString &aAuthorEmail)
wxString GetErrorString() const
GitUserConfig GetUserConfig()
Get user configuration (name and email) from git config Falls back to common settings if not found in...
bool IsRepository(const wxString &aPath)
Check if a directory is already a git repository.
InitResult InitializeRepository(const wxString &aPath)
Initialize a new git repository in the specified directory.
bool SetupRemote(const RemoteConfig &aConfig)
Set up a remote for the repository.
void SetProgressReporter(std::unique_ptr< WX_PROGRESS_REPORTER > aProgressReporter)
bool PerformFetch(bool aSkipLock=false)
void PerformRemoveFromIndex()
bool PerformResolveConflict()
wxString GetCurrentBranchName()
Get the current branch name.
void UpdateRemoteStatus(const std::set< wxString > &aLocalChanges, const std::set< wxString > &aRemoteChanges, std::map< wxString, FileStatus > &aFileStatus)
Get status for modified files based on local/remote changes.
wxString GetWorkingDirectory()
Get the repository working directory path.
bool HasChangedFiles()
Check if the repository has any changed files.
std::map< wxString, FileStatus > GetFileStatus(const wxString &aPathspec=wxEmptyString)
Get detailed file status for all files in the specified path.
The main KiCad project manager frame.
void OnChangeWatchedPaths(wxCommandEvent &aEvent)
Called by sending a event with id = ID_INIT_WATCHED_PATHS rebuild the list of watched paths.
PROJECT_TREE_PANE * m_projectTreePane
static git_repository * GetRepositoryForFile(const char *aFilename)
Discover and open the repository that contains the given file.
static wxString ComputeSymlinkPreservingWorkDir(const wxString &aUserProjectPath, const wxString &aCanonicalWorkDir)
Compute a working directory path that preserves symlinks from the user's project path.
static bool RemoveVCS(git_repository *&aRepo, const wxString &aProjectPath=wxEmptyString, bool aRemoveGitDir=false, wxString *aErrors=nullptr)
Remove version control from a directory by freeing the repository and optionally removing the ....
static int CreateBranch(git_repository *aRepo, const wxString &aBranchName)
Create a new branch based on HEAD.
std::mutex m_gitActionMutex
const wxString & GetRemote() const
GIT_CONN_TYPE GetConnType() const
bool WasAuthFailure() const
void SetSSHKey(const wxString &aSSHKey)
void SetUsername(const wxString &aUsername)
bool HasPushAndPullRemote() const
wxString GetUsername() const
bool HasLocalCommits() const
void SetCancelled(bool aCancel)
void SetPassword(const wxString &aPassword)
wxString GetErrorString()
bool Register(const std::string &aLabel, F &&aWork)
Spawn a tracked orphan thread running aWork.
git_repository * GetRepo() const
Get a pointer to the git repository.
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.
The project local settings are things that are attached to a particular project, but also might be pa...
bool m_GitIntegrationDisabled
If true, KiCad will not use Git integration for this project even if a .git directory exists.
bool SaveToFile(const wxString &aDirectory="", bool aForce=false) override
Calls Store() and then writes the contents of the JSON document to a file.
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
std::unordered_map< wxString, wxTreeItemId > m_gitTreeCache
void onGitFetch(wxCommandEvent &event)
Fetch the latest changes from the git repository.
std::map< wxTreeItemId, KIGIT_COMMON::GIT_STATUS > m_gitStatusIcons
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 ...
wxString m_gitCurrentBranchName
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
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
PROJECT_TREE_PANE(KICAD_MANAGER_FRAME *parent)
KICAD_MANAGER_FRAME * m_Parent
std::future< void > m_gitSyncTask
void onExpand(wxTreeEvent &Event)
Called on a click on the + or - button of an item with children.
void onGitSyncTimer(wxTimerEvent &event)
void onIdle(wxIdleEvent &aEvent)
Idle event handler, used process the selected items at a point in time when all other events have bee...
void updateTreeCache()
Updates the map of the wxtreeitemid to the name of each file for use in the thread.
void gitStatusTimerHandler()
std::mutex m_gitStatusMutex
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 onGitStatusTimer(wxTimerEvent &event)
void shutdownFileWatcher()
Shutdown the file watcher.
std::mutex m_gitTreeCacheMutex
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.
bool m_gitIconsInitialized
std::future< void > m_gitStatusIconTask
void updateGitStatusIconMap()
This is a threaded call that will change the map of git status icons for use in the main thread.
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.
virtual const wxString GetProjectPath() const
Return the full path of the project.
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
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.
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box 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.
GIT_BACKEND * GetGitBackend()
int m_GitIconRefreshInterval
The interval in milliseconds to refresh the git icons in the project tree.
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 CsvFileExtension
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 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.
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.
wxDECLARE_EVENT(wxCUSTOM_PANEL_SHOWN_EVENT, wxCommandEvent)
PGM_BASE & Pgm()
The global program "get" accessor.
#define NAMELESS_PROJECT
default name for nameless projects
project_tree_ids
The frame that shows the tree list of files and subdirectories inside the working directory.
@ ID_PROJECT_SWITCH_TO_OTHER
@ ID_GIT_REMOVE_FROM_INDEX
@ ID_GIT_RESOLVE_CONFLICT
@ ID_GIT_INITIALIZE_PROJECT
std::vector< wxString > getProjects(const wxDir &dir)
static const wxChar * s_allowedExtensionsToList[]
static bool promptForGitCredentials(wxWindow *aParent, KIGIT_COMMON *aCommon)
#define wxFileSystemWatcher
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
KIGIT_COMMON::GIT_CONN_TYPE connType
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
BS::priority_thread_pool thread_pool
wxLogTrace helper definitions.
Definition of file extensions used in Kicad.