28#include <wx/richmsgdlg.h>
29#include <wx/stdpaths.h>
32#include <wx/textdlg.h>
34#include <wx/wupdlock.h>
36#include <wx/dcclient.h>
37#include <wx/progdlg.h>
38#include <wx/settings.h>
39#include <wx/weakref.h>
108 return aTask.valid() && aTask.wait_for( std::chrono::seconds( 0 ) ) != std::future_status::ready;
117 wxT(
"^.*\\.kicad_pro$" ),
120 wxT(
"^.*\\.kicad_sch$" ),
121 wxT(
"^[^$].*\\.brd$" ),
122 wxT(
"^[^$].*\\.kicad_pcb$" ),
123 wxT(
"^[^$].*\\.kicad_dru$" ),
124 wxT(
"^[^$].*\\.kicad_wks$" ),
125 wxT(
"^[^$].*\\.kicad_mod$" ),
129 wxT(
"^.*\\.kicad_sym$" ),
134 wxT(
"^.*\\.gbrjob$" ),
135 wxT(
"^.*\\.gb[alops]$" ),
136 wxT(
"^.*\\.gt[alops]$" ),
137 wxT(
"^.*\\.g[0-9]{1,2}$" ),
138 wxT(
"^.*\\.gm[0-9]{1,2}$" ),
142 wxT(
"^.*\\.html$" ),
153 wxT(
"^.*\\.kicad_jobset" ),
215 wxSashLayoutWindow( parent,
ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxTAB_TRAVERSAL ),
226 Bind( wxEVT_FSWATCHER,
229 Bind( wxEVT_SYS_COLOUR_CHANGED,
243 m_filters.emplace_back( wxT(
"^no KiCad files found" ) );
253 Unbind( wxEVT_FSWATCHER,
255 Unbind( wxEVT_SYS_COLOUR_CHANGED,
265 common->RemoveRefreshListener(
this );
291 if( tree_data.size() != 1 )
294 wxString prj_filename = tree_data[0]->GetFileName();
296 m_Parent->LoadProject( prj_filename );
308 wxString curr_dir = item_data->GetDir();
310 if( curr_dir.IsEmpty() )
313 curr_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
316 if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
319 if( !curr_dir.IsEmpty() )
320 curr_dir += wxFileName::GetPathSeparator();
335 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
338 wxString curr_dir = item_data->GetDir();
340 if( curr_dir.IsEmpty() )
343 wxString new_dir = wxGetTextFromUser(
_(
"Directory name:" ),
_(
"Create New Directory" ) );
345 if( new_dir.IsEmpty() )
348 wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
350 if( !wxMkdir( full_dirname ) )
398 return wxEmptyString;
404 std::vector<wxString> projects;
405 wxString dir_filename;
406 bool haveFile = dir.GetFirst( &dir_filename );
410 wxFileName file( dir_filename );
414 projects.push_back( file.GetName() );
416 haveFile = dir.GetNext( &dir_filename );
427 const wxTreeItemId& aParent,
428 std::vector<wxString>* aProjectNames,
432 wxFileName fn( aName );
435 return wxTreeItemId();
438 if( fn.GetFullName().IsSameAs( wxS(
".git" ),
false ) )
439 return wxTreeItemId();
441 if( wxDirExists( aName ) )
449 bool addFile =
false;
451 for(
const wxString& m_filter :
m_filters )
453 wxCHECK2_MSG( reg.Compile( m_filter, wxRE_ICASE ),
continue,
454 wxString::Format(
"Regex %s failed to compile.", m_filter ) );
456 if( reg.Matches( aName ) )
464 return wxTreeItemId();
471 if( ext == wxT(
"" ) )
474 if( reg.Compile( wxString::FromAscii(
"^.*\\." ) + ext + wxString::FromAscii(
"$" ), wxRE_ICASE )
475 && reg.Matches( aName ) )
483 wxString file = wxFileNameFromPath( aName );
484 wxFileName currfile( file );
486 bool showAllSchematics =
gitRepo() !=
nullptr;
490 && ( currfile.GetName().CmpNoCase(
project.GetName() ) == 0 ) )
492 return wxTreeItemId();
501 return wxTreeItemId();
507 if( !parentTreeItem )
508 return wxTreeItemId();
510 wxDir parentDir( parentTreeItem->
GetDir() );
511 std::vector<wxString> projects =
getProjects( parentDir );
514 return wxTreeItemId();
519 wxTreeItemIdValue cookie;
520 wxTreeItemId kid =
m_TreeProject->GetFirstChild( aParent, cookie );
527 return itemData->GetId();
548 if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
554 return wxTreeItemId();
560 return wxTreeItemId();
587 wxTreeItemId newItemId =
m_TreeProject->AppendItem( aParent, file );
594 wxString fileName = currfile.GetName().Lower();
595 wxString projName =
project.GetName().Lower();
597 if( fileName == projName || fileName.StartsWith( projName +
"-" ) )
601 bool subdir_populated =
false;
612 std::vector<wxString> projects =
getProjects( dir );
613 wxString dir_filename;
614 bool haveFile = dir.GetFirst( &dir_filename );
619 subdir_populated = aRecurse;
625 wxString
path = aName + wxFileName::GetPathSeparator() + dir_filename;
628 haveFile = dir.GetNext( &dir_filename );
637 if( subdir_populated )
656 wxString pro_dir =
m_Parent->GetProjectFileName();
670 if( !
Pgm().GetCommonSettings()->m_Git.enableGit )
676 wxFileName fn = pro_dir;
677 bool prjReset =
false;
688 bool prjOpened = fn.FileExists();
690 if(
Pgm().GetCommonSettings()->m_Git.enableGit )
698 if( !prjOpened && !prjReset )
701 prjOpened = fn.FileExists();
721 pro_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
722 wxDir dir( pro_dir );
726 std::vector<wxString> projects =
getProjects( dir );
728 bool haveFile = dir.GetFirst( &filename );
732 if( filename != fn.GetFullName() )
734 wxString
name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
741 haveFile = dir.GetNext( &filename );
758 wxLogTrace(
traceGit,
"PROJECT_TREE_PANE::ReCreateTreePrj: starting timers" );
767 wxTreeItemId curr_item = Event.GetItem();
786 bool can_switch_to_project =
true;
787 bool can_create_new_directory =
true;
788 bool can_open_this_directory =
true;
789 bool can_edit =
true;
790 bool can_rename =
true;
791 bool can_delete =
true;
792 bool run_jobs =
false;
795 bool vcs_enabled = !gitIntegrationDisabled;
796 bool vcs_has_repo =
gitRepo() !=
nullptr;
797 bool vcs_can_init = vcs_enabled && !vcs_has_repo;
798 bool vcs_can_set_remote = vcs_enabled && vcs_has_repo;
801 bool vcs_can_remove = vcs_has_repo || gitIntegrationDisabled;
802 bool vcs_can_switch = vcs_enabled && vcs_has_repo;
808 vcs_menu &= libgit_init;
810 if( selection.size() == 0 )
814 if( selection.size() != 1 )
816 can_switch_to_project =
false;
817 can_create_new_directory =
false;
826 can_switch_to_project =
false;
832 can_delete = item->CanDelete();
833 can_rename = item->CanRename();
835 switch( item->GetType() )
843 can_switch_to_project =
false;
847 can_create_new_directory =
false;
848 can_open_this_directory =
false;
853 can_switch_to_project =
false;
860 can_switch_to_project =
false;
861 can_create_new_directory =
false;
862 can_open_this_directory =
false;
875 can_switch_to_project =
false;
876 can_create_new_directory =
false;
877 can_open_this_directory =
false;
887 if( can_switch_to_project )
890 _(
"Close all editors, and switch to the selected project" ),
892 popup_menu.AppendSeparator();
895 if( can_create_new_directory )
901 if( can_open_this_directory )
903 if( selection.size() == 1 )
906 text =
_(
"Reveal in Finder" );
907 help_text =
_(
"Reveals the directory in a Finder window" );
909 text =
_(
"Open Directory in File Explorer" );
910 help_text =
_(
"Opens the directory in the default system file manager" );
916 text =
_(
"Reveal in Finder" );
917 help_text =
_(
"Reveals the directories in a Finder window" );
919 text =
_(
"Open Directories in File Explorer" );
920 help_text =
_(
"Opens the directories in the default system file manager" );
930 if( selection.size() == 1 )
931 help_text =
_(
"Open the file in a Text Editor" );
933 help_text =
_(
"Open files in a Text Editor" );
939 if( run_jobs && selection.size() == 1 )
947 if( selection.size() == 1 )
949 text =
_(
"Rename File..." );
950 help_text =
_(
"Rename file" );
954 text =
_(
"Rename Files..." );
955 help_text =
_(
"Rename files" );
964 if( selection.size() == 1 )
965 help_text =
_(
"Delete the file and its content" );
967 help_text =
_(
"Delete the files and their contents" );
969 if( can_switch_to_project
970 || can_create_new_directory
971 || can_open_this_directory
975 popup_menu.AppendSeparator();
989 wxMenu* vcs_submenu =
new wxMenu();
990 wxMenu* branch_submenu =
new wxMenu();
991 wxMenuItem* vcs_menuitem =
nullptr;
994 _(
"Add Project to Version Control..." ),
995 _(
"Initialize a new repository" ) );
996 vcs_menuitem->Enable( vcs_can_init );
999 _(
"Set or change the default remote URL and credentials" ) );
1000 vcs_menuitem->Enable( vcs_can_set_remote );
1004 vcs_submenu->AppendSeparator();
1008 std::shared_ptr<KIGIT_COMMON> common =
gitCommon();
1011 std::vector<KIGIT_BRANCH_INFO> branches;
1014 std::unique_lock<std::mutex> branchLock = common->TryLockAction();
1016 if( branchLock.owns_lock() )
1017 branches = common->GetBranches();
1024 if( branch.isRemote || branch.isCurrent )
1037 vcs_menuitem->Enable( vcs_can_switch );
1041 _(
"Switch to a different branch" ) );
1042 vcs_menuitem->Enable( vcs_can_switch );
1044 vcs_menuitem = vcs_submenu->Append(
ID_GIT_SWITCH_BRANCH,
_(
"Switch to Branch" ), branch_submenu );
1045 vcs_menuitem->Enable( vcs_can_switch );
1047 vcs_submenu->AppendSeparator();
1049 if( gitIntegrationDisabled )
1051 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Enable Git Integration" ),
1052 _(
"Re-enable Git integration for this project" ) );
1056 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Disable Git Integration" ),
1057 _(
"Disable Git integration for this project" ) );
1060 vcs_menuitem->Enable( vcs_can_remove );
1062 popup_menu.AppendSeparator();
1063 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
1066 if( popup_menu.GetMenuItemCount() > 0 )
1067 PopupMenu( &popup_menu );
1075 if( editorname.IsEmpty() )
1077 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1085 wxString fullFileName = item_data->GetFileName();
1087 if( !fullFileName.IsEmpty() )
1089 ExecuteFile( editorname, fullFileName.wc_str(),
nullptr,
false );
1100 item_data->Delete();
1110 if( tree_data.size() != 1 )
1114 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1115 tree_data[0]->GetFileName() );
1116 wxTextEntryDialog dlg( wxGetTopLevelParent(
this ), msg,
_(
"Change filename" ), buffer );
1118 if( dlg.ShowModal() != wxID_OK )
1121 buffer = dlg.GetValue();
1122 buffer.Trim(
true );
1123 buffer.Trim(
false );
1125 if( buffer.IsEmpty() )
1128 tree_data[0]->Rename( buffer,
true );
1137 if( tree_data.size() != 1 )
1160 std::vector<wxTreeItemId> validItemIds;
1163 for( wxTreeItemId
id : validItemIds )
1182 wxTreeItemId itemId = Event.GetItem();
1192 wxTreeItemIdValue cookie;
1193 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1196 bool subdir_populated =
false;
1199 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1210 wxDir dir( fileName );
1212 if( dir.IsOpened() )
1214 std::vector<wxString> projects =
getProjects( dir );
1215 wxString dir_filename;
1216 bool haveFile = dir.GetFirst( &dir_filename );
1221 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1224 haveFile = dir.GetNext( &dir_filename );
1230 subdir_populated =
true;
1239 if( subdir_populated )
1247 wxArrayTreeItemIds selection;
1248 std::vector<PROJECT_TREE_ITEM*> data;
1252 for(
const wxTreeItemId itemId : selection )
1258 wxLogTrace(
traceGit, wxS(
"Null tree item returned for selection, dynamic_cast failed?" ) );
1262 data.push_back( item );
1277 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
1281 if( prj_dir == aSubDir )
1285 wxTreeItemIdValue cookie;
1286 wxTreeItemId root_id =
m_root;
1287 std::stack<wxTreeItemId> subdirs_id;
1289 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1293 if( ! child.IsOk() )
1295 if( subdirs_id.empty() )
1302 root_id = subdirs_id.top();
1323 subdirs_id.push( child );
1341 if( !( event.GetChangeType() & ( wxFSW_EVENT_CREATE |
1342 wxFSW_EVENT_DELETE |
1343 wxFSW_EVENT_RENAME |
1344 wxFSW_EVENT_MODIFY ) ) )
1349 const wxFileName& pathModified =
event.GetPath();
1352 if( pathModified.GetFullPath().Contains( wxS(
".history" ) ) )
1355 wxString subdir = pathModified.GetPath();
1356 wxString fn = pathModified.GetFullPath();
1359 if( pathModified.GetFullName().IsEmpty() )
1361 subdir = subdir.BeforeLast(
'/' );
1362 fn = fn.BeforeLast(
'/' );
1367 if( !root_id.IsOk() )
1370 CallAfter( [
this] ()
1372 wxLogTrace(
traceGit, wxS(
"File system event detected, updating tree cache" ) );
1376 wxTreeItemIdValue cookie;
1377 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1379 switch( event.GetChangeType() )
1381 case wxFSW_EVENT_CREATE:
1396 case wxFSW_EVENT_DELETE:
1410 case wxFSW_EVENT_RENAME :
1412 const wxFileName& newpath =
event.GetNewPath();
1413 wxString newdir = newpath.GetPath();
1414 wxString newfn = newpath.GetFullPath();
1438 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1444 if( newitem.IsOk() )
1465 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
1467#if defined( _WIN32 )
1474 m_Parent->m_FileWatcherInfo =
_(
"Network path: not monitoring folder changes" );
1480 m_Parent->m_FileWatcherInfo =
_(
"Local path: monitoring folder changes" );
1498 class WatcherLogHandler :
public wxLog
1501 explicit WatcherLogHandler(
bool* err ) :
1509 void DoLogTextAtLevel( wxLogLevel level,
const wxString&
text )
override
1511 if( m_err && ( level == wxLOG_Error || level == wxLOG_FatalError ) )
1519 bool watcherHasError =
false;
1520 WatcherLogHandler tmpLog( &watcherHasError );
1529 if( watcherHasError )
1537 fn.AssignDir( prj_dir );
1538 fn.DontFollowLink();
1552 TO_UTF8( fn.GetFullPath() ) );
1560 TO_UTF8( fn.GetFullPath() ) );
1565 if( m_TreeProject->IsEmpty() )
1569 wxTreeItemIdValue cookie;
1570 wxTreeItemId root_id = m_root;
1572 std::stack < wxTreeItemId > subdirs_id;
1574 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1575 int total_watch_count = 0;
1581 if( subdirs_id.empty() )
1587 root_id = subdirs_id.top();
1589 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1604 if(
path.Contains( wxS(
".history" ) ) )
1606 kid = m_TreeProject->GetNextChild( root_id, cookie );
1612 if( wxFileName::IsDirReadable(
path ) )
1617 fn.AssignDir(
path );
1618 m_watcher->Add( fn );
1619 total_watch_count++;
1623 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1624 subdirs_id.push( kid );
1628 kid = m_TreeProject->GetNextChild( root_id, cookie );
1635#if defined(DEBUG) && 1
1636 wxArrayString paths;
1637 m_watcher->GetWatchedPaths( &paths );
1640 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1666 std::shared_ptr<KIGIT_COMMON> common =
gitCommon();
1667 common->SetShuttingDown(
true );
1669 std::unique_lock<std::mutex> lock = common->TryLockAction();
1671 constexpr auto kGraceMs = std::chrono::seconds( 2 );
1672 auto graceEnd = std::chrono::steady_clock::now() + kGraceMs;
1674 while( !lock.owns_lock() && std::chrono::steady_clock::now() < graceEnd )
1676 if( lock.try_lock() )
1678 std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
1681 constexpr auto kCheckInterval = std::chrono::seconds( 30 );
1682 bool userAbandoned =
false;
1684 while( !lock.owns_lock() && !userAbandoned )
1686 auto intervalEnd = std::chrono::steady_clock::now() + kCheckInterval;
1689 wxProgressDialog progress(
_(
"Please wait" ),
1690 _(
"Closing project..." ),
1692 wxPD_APP_MODAL | wxPD_SMOOTH );
1694 while( !lock.try_lock()
1695 && std::chrono::steady_clock::now() < intervalEnd )
1698 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
1703 if( lock.owns_lock() )
1706 wxMessageDialog ask(
this,
1707 _(
"A Git operation is still running.\n"
1708 "Keep waiting, or abandon?" ),
1709 _(
"Git Operation Delayed" ),
1710 wxYES_NO | wxICON_QUESTION );
1711 ask.SetYesNoLabels(
_(
"Keep Waiting" ),
_(
"Abandon" ) );
1713 if( ask.ShowModal() == wxID_NO )
1714 userAbandoned =
true;
1728 common->SetRepo(
nullptr );
1751 std::shared_ptr<KIGIT_COMMON> common =
gitCommon();
1762 previous->RemoveRefreshListener(
this );
1766 common->AddRefreshListener(
this,
1795 wxRect rect( wxPoint( 0, 0 ), GetClientSize() );
1796 wxPaintDC dc(
this );
1798 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_FRAMEBK ) );
1799 dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_ACTIVEBORDER ), 1 ) );
1801 dc.DrawLine( rect.GetLeft(), rect.GetTop(), rect.GetLeft(), rect.GetBottom() );
1802 dc.DrawLine( rect.GetRight(), rect.GetTop(), rect.GetRight(), rect.GetBottom() );
1842 wxString dir = tree_data->
GetDir();
1846 wxLogError(
"Failed to initialize git project: project directory is empty." );
1851 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1856 _(
"The selected directory is already a Git project." ) );
1872 const char* canonicalWorkDir = git_repository_workdir( initHandler.
GetRepo() );
1874 if( canonicalWorkDir )
1877 dir, wxString::FromUTF8( canonicalWorkDir ) );
1882 dlg.SetTitle(
_(
"Set default remote" ) );
1909 bool retriedAfterTrust =
false;
1915 std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetch Remote" ), 1,
PR_NO_ABORT ) );
1923 if( std::optional<KIGIT::HOST_KEY_FAILURE> hostKey = handler.
GetHostKeyFailure() )
1928 retriedAfterTrust =
true;
1960 wxString existingURL;
1966 if( !lock.owns_lock() )
1969 git_remote* remote =
nullptr;
1971 if( git_remote_lookup( &remote, repo,
"origin" ) == GIT_OK )
1975 if(
const char* url = git_remote_url( remote ) )
1976 existingURL = wxString::FromUTF8( url );
1980 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1982 dlg.SetTitle(
_(
"Configure Default Remote" ) );
2004 bool setupOk =
false;
2005 wxString setupError;
2006 wxString setupDetails;
2012 if( !lock.owns_lock() )
2015 setupOk = initHandler.
SetupRemote( remoteConfig );
2038 std::shared_ptr<KIGIT_COMMON> common =
gitCommon();
2040 if( !common->GetRepo() || common->IsShuttingDown() )
2047 std::vector<KIGIT_BRANCH_INFO> entries;
2048 bool loaded =
false;
2051 auto branches = std::make_shared<BRANCHES>();
2052 wxWeakRef<PROJECT_TREE_PANE> self(
this );
2059 branches->loaded =
true;
2061 [self, common, branches]()
2063 if( !branches->loaded || !self || self->IsBeingDeleted() || self->m_Parent->IsBeingDeleted()
2064 || common->IsShuttingDown() || self->gitCommon() != common
2065 || !
Pgm().GetCommonSettings()->m_Git.enableGit
2066 ||
Prj().GetLocalSettings().m_GitIntegrationDisabled )
2071 DIALOG_GIT_SWITCH dlg( wxGetTopLevelParent( self->m_Parent ), std::move( branches->entries ) );
2074 if( !self || self->IsBeingDeleted() || common->IsShuttingDown() || self->gitCommon() != common )
2077 if( retval == wxID_OK || retval == wxID_ADD )
2078 self->switchGitBranch( common, dlg.
GetBranchName(), retval == wxID_ADD );
2099 if( IsBeingDeleted() ||
m_Parent->IsBeingDeleted() ||
gitCommon() != aCommon || aCommon->IsShuttingDown()
2100 || !
Pgm().GetCommonSettings()->m_Git.enableGit ||
Prj().GetLocalSettings().m_GitIntegrationDisabled )
2121 if( IsBeingDeleted() ||
m_Parent->IsBeingDeleted() ||
gitCommon() != aCommon || aCommon->IsShuttingDown()
2122 || !
Pgm().GetCommonSettings()->m_Git.enableGit ||
Prj().GetLocalSettings().m_GitIntegrationDisabled )
2133 if( !lock.owns_lock() )
2147 aCommon->NotifyRefreshListeners();
2161 wxLogTrace(
traceGit, wxS(
"onGitRemoveVCS: Git integration %s" ),
2171 std::stack<wxTreeItemId> items;
2174 while( !items.empty() )
2176 wxTreeItemId current = items.top();
2179 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
2181 wxTreeItemIdValue cookie;
2182 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
2184 while( child.IsOk() )
2186 items.push( child );
2204 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Updating git status icons" ) );
2207 if( !lock.owns_lock() )
2209 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Failed to acquire lock for git status icon update" ) );
2216 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Git is disabled or tree control is null" ) );
2220 std::stack<wxTreeItemId> items;
2223 while( !items.empty() )
2225 wxTreeItemId current = items.top();
2228 if( current.IsOk() &&
m_TreeProject->ItemHasChildren( current ) )
2230 wxTreeItemIdValue cookie;
2231 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
2233 while( child.IsOk() )
2235 items.push( child );
2238 m_TreeProject->SetItemState( child,
static_cast<int>( it->second ) );
2252 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
2258 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Git status icons updated" ) );
2264 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Updating tree cache" ) );
2268 if( !lock.owns_lock() )
2270 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Failed to acquire lock for tree cache update" ) );
2276 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Tree control is null" ) );
2287 std::stack<wxTreeItemId> items;
2290 while( !items.empty() )
2302 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2306 wxTreeItemIdValue cookie;
2307 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
2309 while( child.IsOk() )
2311 items.push( child );
2319 const wxString& aProjectFullName )
2321 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Updating git status icons" ) );
2322#if defined( _WIN32 )
2344 if( !lock1.owns_lock() || !lock2.owns_lock() )
2346 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Failed to acquire locks for git status icon update" ) );
2352 if( !aGitCommon || !aGitCommon->GetRepo() )
2354 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: No git repository found" ) );
2360 std::unique_lock<std::mutex> gitLock = aGitCommon->TryLockAction();
2362 if( !gitLock.owns_lock() )
2364 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Failed to acquire git action mutex" ) );
2369 if( aGitCommon->IsShuttingDown() )
2371 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Cancelled" ) );
2379 wxFileName rootFilename( aProjectFullName );
2382 wxFileName relative = rootFilename;
2383 relative.MakeRelativeTo( repoWorkDir );
2384 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
2387 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
2391 auto fileStatusMap = statusHandler.
GetFileStatus( pathspecStr );
2392 auto [localChanges, remoteChanges] = aGitCommon->GetDifferentFiles();
2395 bool updated =
false;
2398 for(
const auto& [absPath, fileStatus] : fileStatusMap )
2403 wxLogTrace(
traceGit, wxS(
"File '%s' not found in tree cache" ), absPath );
2407 auto [it, inserted] =
m_gitStatusIcons.try_emplace( iter->second, fileStatus.status );
2408 if( inserted || it->second != fileStatus.status )
2410 it->second = fileStatus.status;
2421 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Updated git status icons" ) );
2427 [
this, aGitCommon]()
2434 aGitCommon->NotifyRefreshListeners();
2447 wxLogTrace(
traceGit,
"onGitSyncTimer" );
2457 wxLogTrace(
traceGit,
"onGitSyncTimer: previous fetch still running, skipping" );
2464 std::shared_ptr<KIGIT_COMMON> common =
gitCommon();
2471 if( !common->GetRepo() )
2473 wxLogTrace(
traceGit,
"onGitSyncTimer: No git repository found" );
2478 if( common->IsShuttingDown() )
2480 wxLogTrace(
traceGit,
"onGitSyncTimer: Cancelled" );
2490 if( !common->IsShuttingDown() )
2492 [
this, common, fetched, kind, summary]()
2495 if( common->IsShuttingDown() || common !=
gitCommon() )
2503 common->NotifyRefreshListeners();
2507 wxLogTrace(
traceGit,
"onGitSyncTimer: Restarting git sync timer" );
2514 const wxString& aSummary )
2534 if( existing.GetTitle() == aSummary )
2552 std::shared_ptr<KIGIT_COMMON> common =
gitCommon();
2554 if( common->IsShuttingDown() )
2568 [
this, common, projectFullName]()
2576 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.
void SetRepoSSHPath(const wxString &aPath)
KIGIT_COMMON::GIT_CONN_TYPE GetRepoType() const
void SetPassword(const wxString &aPassword)
wxString GetRepoSSHPath() const
wxString GetRepoURL() const
wxString GetUsername() const
void SetUsername(const wxString &aUsername)
wxString GetPassword() const
void SetSkipButtonLabel(const wxString &aLabel)
wxString GetBranchName() const
virtual bool IsLibraryAvailable()=0
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< PROGRESS_REPORTER > aProgressReporter)
bool PerformFetch(bool aSkipLock=false)
std::map< wxString, FileStatus > GetFileStatus(const wxString &aPathspec=wxEmptyString, bool aChangedOnly=false)
Get detailed file status for all files in the specified path.
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.
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 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.
The project's git session.
void SetUsername(const wxString &aUsername)
git_repository * GetRepo() const
void SetProjectDir(const wxString &aProjectDir)
Keep symlinks in the repository path so Git status paths match the project tree.
static constexpr std::chrono::milliseconds ACTION_LOCK_WAIT
Long enough for a status read to finish, short enough that a window does not look hung.
wxString GetUsername() const
void SetShuttingDown(bool aCancel)
Stop the operation running now and keep later ones from starting.
static wxString GetLastGitError()
libgit2's last message, without credentials from any URL it quotes, so it is safe to show or trace.
void UpdateCurrentBranchInfo()
static REMOTE_URL ParseRemoteURL(const wxString &aURL)
std::vector< KIGIT_BRANCH_INFO > GetBranches() const
Local and remote branches with their tip commits.
void SetRemote(const wxString &aRemote)
KIGIT_ERROR_MESSAGE PopError()
Take the newest error with its details. Both are empty when none is queued.
Read repository state without blocking the UI.
std::optional< KIGIT::HOST_KEY_FAILURE > GetHostKeyFailure() const
The host key this operation's transfer refused, for the UI to offer to trust or to report.
git_repository * GetRepo() const
Get a pointer to the git repository.
void SetActionLockWait(std::chrono::milliseconds aWait)
A UI caller retrying confirmed input waits this long for a background read instead of failing at once...
std::optional< KIGIT_FAILURE > GetFailureKind() const
The kind of the last failure ReportTransferFailure classified, or none.
bool WasAuthFailure() const
Set when the server refused every credential offered, so the caller can prompt and retry.
KISTATUSBAR is a wxStatusBar suitable for Kicad manager.
void AddWarningMessages(const wxString &aSource, const wxString &aMessages)
Add warning/error messages (not thread-safe, use the std::vector<KI_ERROR> variant from other threads...
std::map< wxString, std::vector< KI_ERROR > > GetWarningMessages() const
Get a copy of all stored warning/error messages, grouped by source and sorted by source name for stab...
Holds a structured error message.
KI_ERROR & SetSeverity(SEVERITY aSeverity)
KI_ERROR & SetTitle(const wxString &aTitle)
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)
Window to display the tree files.
PROJECT_TREE_ITEM * m_selectedItem
void switchGitBranch(std::shared_ptr< KIGIT_COMMON > aCommon, const wxString &aBranchName, bool aCreate)
void syncGitListener()
Follow the project's git session with a refresh listener, so operations started from the version cont...
std::unordered_map< wxString, wxTreeItemId > m_gitTreeCache
std::map< wxTreeItemId, KIGIT_COMMON::GIT_STATUS > m_gitStatusIcons
void onGitInitializeProject(wxCommandEvent &event)
Initialize a new git repository in the current project directory.
git_repository * gitRepo() const
void onDeleteFile(wxCommandEvent &event)
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()
Return the item data from item currently selected (highlighted)
void onOpenDirectory(wxCommandEvent &event)
Handles the right-click menu for opening a directory in the current system file browser.
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 WaitForGitOperations()
Give a running background git operation a chance to finish, asking the user to keep waiting or abando...
void onRight(wxTreeEvent &Event)
Called on a right click on an item.
void onSelect(wxTreeEvent &Event)
Called on a double click on an item.
PROJECT_TREE * m_TreeProject
PROJECT_TREE_PANE(KICAD_MANAGER_FRAME *parent)
std::weak_ptr< KIGIT_COMMON > m_gitMenuSession
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.
std::weak_ptr< KIGIT_COMMON > m_gitListenerSession
std::vector< wxString > m_gitMenuBranches
Preserve the shortcut targets while the context menu runs its event loop.
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
static wxString GetFileExt(TREE_FILE_TYPE type)
friend class PROJECT_TREE_ITEM
wxTreeItemId findSubdirTreeItem(const wxString &aSubDir)
Search for the item in tree project which is the node of the subdirectory aSubDir.
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 onGitStatusTimer(wxTimerEvent &event)
void shutdownFileWatcher()
Shutdown the file watcher.
std::mutex m_gitTreeCacheMutex
std::shared_ptr< KIGIT_COMMON > gitCommon() const
The current project's git session, created empty on first use.
void updateGitStatusIconMap(std::shared_ptr< KIGIT_COMMON > aGitCommon, const wxString &aProjectFullName)
This is a threaded call that will change the map of git status icons for use in the main thread.
wxTreeItemId addItemToProjectTree(const wxString &aName, const wxTreeItemId &aParent, std::vector< wxString > *aProjectNames, bool aRecurse)
Add the file or directory aName to the project tree.
void onGitRemoteSettings(wxCommandEvent &event)
Configure (or change) the default remote on an already-initialized repository.
void onOpenSelectedFileWithTextEditor(wxCommandEvent &event)
Call the text editor to open the selected file in the tree project.
void updateGitStatusIcons()
Updates the icons shown in the tree project to reflect the current git status.
bool m_gitIconsInitialized
std::future< void > m_gitStatusIconTask
PROJECT_TREE_ITEM * GetItemIdData(wxTreeItemId aId)
Return the item data corresponding to a wxTreeItemId identifier.
std::unique_ptr< KIGIT_QUERY > m_branchQuery
Owns the branch-switch preflight and suppresses callbacks when the project closes.
void reportGitSyncResult(bool aFetched, std::optional< KIGIT_FAILURE > aKind, const wxString &aSummary)
Background fetches have no window to answer, so each failure goes to the status bar's warnings once,...
void onGitSwitchBranch(wxCommandEvent &event)
Switch to a different branch in the git repository.
void onCreateNewDirectory(wxCommandEvent &event)
Creates a new subdirectory inside the current kicad project directory the user is prompted to enter a...
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)
Rename the selected file or directory in the tree project.
void releaseGitRepo()
Releases the git repository handle, deferring the free to the orphan registry while a background git ...
std::vector< wxString > m_filters
PROJECT_TREE This is the class to show (as a tree) the files in the project directory.
Container for project specific data.
virtual std::shared_ptr< KIGIT_COMMON > GetGitCommon() const
This project's Git session, created empty on first use.
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
virtual bool AttachGitRepository()
Find the Git repository containing the project directory and bind it to the session.
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
virtual void ReleaseGitCommon()
Safely release the session's repository handle and drop the session.
A scoped application of a wxLog target.
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.
wxDECLARE_EVENT(wxEVT_REFRESH_CUSTOM_COMMAND, wxCommandEvent)
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()
void ProjectClearOfUncommittedWork(EDA_BASE_FRAME *aFrame, wxWindow *aParent, KIGIT_QUERY &aQuery, std::function< void(const GIT_WORKTREE_STATUS &)> aReady)
Check the working tree and Git LFS on a worker, then run aReady on the UI thread when the operation c...
void SwitchProjectToBranch(EDA_BASE_FRAME *aFrame, wxWindow *aParent, const wxString &aBranch, KIGIT_QUERY &aQuery, std::function< void()> aFinished)
Check the working tree on a worker, then confirm switching to aBranch and recheck unsaved editors.
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 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 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 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.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
EVT_MENU(ID_COMPARE_PROJECT_BRANCHES, KICAD_MANAGER_FRAME::OnCompareProjectBranches) KICAD_MANAGER_FRAME
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 ...
void ClearSyncWarnings(wxWindow *aWindow)
Clear the background fetch's warnings from the frame holding aWindow.
void SortBranchesForDisplay(std::vector< KIGIT_BRANCH_INFO > &aBranches)
Put the current branch first, then local and remote branches with the newest tips first.
wxString BranchNameError(const wxString &aName)
Empty when aName is a branch name git would accept, otherwise why it would not, in the words to show ...
constexpr const wxChar * SYNC_WARNING_SOURCE
The status bar source under which background fetch failures are listed.
std::unique_lock< std::mutex > WaitForActionLock(KIGIT_COMMON &aCommon, wxWindow *aParent)
TryLockAction that first waits a moment for a status read or fetch to let go.
bool PromptToTrustHost(wxWindow *aParent, const HOST_KEY_FAILURE &aFailure, bool aRetriedAfterTrust)
Ask whether to trust a server seen for the first time, and remember its key when the user agrees.
std::unique_lock< std::mutex > TryLockAction(KIGIT_COMMON &aCommon, wxWindow *aParent)
Try the action lock and show a busy message if it is unavailable.
bool PromptForCredentials(wxWindow *aParent, KIGIT_COMMON *aCommon, PROJECT *aProject=nullptr, const wxString &aExplanation=wxEmptyString)
Ask the user for the credentials of the session's remote and apply them to the session.
std::unique_ptr< git_remote, decltype([](git_remote *aRemote) { git_remote_free(aRemote); })> GitRemotePtr
A unique pointer for git_remote objects with automatic cleanup.
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
static bool gitTaskStillRunning(const std::future< void > &aTask)
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_INITIALIZE_PROJECT
static bool promptForGitCredentials(wxWindow *aParent, KIGIT_COMMON *aCommon, const wxString &aExplanation)
static void saveAcceptedRemoteSettings(PROJECT &aProject, const RemoteConfig &aConfig)
std::vector< wxString > getProjects(const wxDir &dir)
static const wxChar * s_allowedExtensionsToList[]
#define wxFileSystemWatcher
Repository state used before an operation that requires a clean working tree.
A summary the user can act on, with the raw text that explains it kept apart for the details.
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.