30#include <wx/stdpaths.h>
33#include <wx/textdlg.h>
35#include <wx/wupdlock.h>
57#include <wx/dcclient.h>
58#include <wx/progdlg.h>
59#include <wx/settings.h>
108 wxT(
"^.*\\.kicad_pro$" ),
111 wxT(
"^.*\\.kicad_sch$" ),
112 wxT(
"^[^$].*\\.brd$" ),
113 wxT(
"^[^$].*\\.kicad_pcb$" ),
114 wxT(
"^[^$].*\\.kicad_dru$" ),
115 wxT(
"^[^$].*\\.kicad_wks$" ),
116 wxT(
"^[^$].*\\.kicad_mod$" ),
120 wxT(
"^.*\\.kicad_sym$" ),
125 wxT(
"^.*\\.gbrjob$" ),
126 wxT(
"^.*\\.gb[alops]$" ),
127 wxT(
"^.*\\.gt[alops]$" ),
128 wxT(
"^.*\\.g[0-9]{1,2}$" ),
129 wxT(
"^.*\\.gm[0-9]{1,2}$" ),
133 wxT(
"^.*\\.html$" ),
144 wxT(
"^.*\\.kicad_jobset" ),
227 wxSashLayoutWindow( parent,
ID_LEFT_FRAME, wxDefaultPosition, wxDefaultSize, wxNO_BORDER | wxTAB_TRAVERSAL )
238 Bind( wxEVT_FSWATCHER,
241 Bind( wxEVT_SYS_COLOUR_CHANGED,
255 m_filters.emplace_back( wxT(
"^no KiCad files found" ) );
263 Unbind( wxEVT_FSWATCHER,
265 Unbind( wxEVT_SYS_COLOUR_CHANGED,
298 if( tree_data.size() != 1 )
301 wxString prj_filename = tree_data[0]->GetFileName();
303 m_Parent->LoadProject( prj_filename );
315 wxString curr_dir = item_data->GetDir();
317 if( curr_dir.IsEmpty() )
320 curr_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
323 if( curr_dir.IsEmpty() || !wxFileName::DirExists( curr_dir ) )
326 if( !curr_dir.IsEmpty() )
327 curr_dir += wxFileName::GetPathSeparator();
342 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
345 wxString curr_dir = item_data->GetDir();
347 if( curr_dir.IsEmpty() )
350 wxString new_dir = wxGetTextFromUser(
_(
"Directory name:" ),
_(
"Create New Directory" ) );
352 if( new_dir.IsEmpty() )
355 wxString full_dirname = curr_dir + wxFileName::GetPathSeparator() + new_dir;
357 if( !wxMkdir( full_dirname ) )
405 return wxEmptyString;
411 std::vector<wxString> projects;
412 wxString dir_filename;
413 bool haveFile = dir.GetFirst( &dir_filename );
417 wxFileName file( dir_filename );
421 projects.push_back( file.GetName() );
423 haveFile = dir.GetNext( &dir_filename );
434 const wxTreeItemId& aParent,
435 std::vector<wxString>* aProjectNames,
439 wxFileName fn( aName );
442 return wxTreeItemId();
444 if( wxDirExists( aName ) )
452 bool addFile =
false;
454 for(
const wxString& m_filter :
m_filters )
456 wxCHECK2_MSG( reg.Compile( m_filter, wxRE_ICASE ),
continue,
457 wxString::Format(
"Regex %s failed to compile.", m_filter ) );
459 if( reg.Matches( aName ) )
467 return wxTreeItemId();
474 if( ext == wxT(
"" ) )
477 if( reg.Compile( wxString::FromAscii(
"^.*\\." ) + ext + wxString::FromAscii(
"$" ), wxRE_ICASE )
478 && reg.Matches( aName ) )
486 wxString file = wxFileNameFromPath( aName );
487 wxFileName currfile( file );
489 bool showAllSchematics =
m_TreeProject->GetGitRepo() !=
nullptr;
493 && ( currfile.GetName().CmpNoCase(
project.GetName() ) == 0 ) )
495 return wxTreeItemId();
504 return wxTreeItemId();
510 if( !parentTreeItem )
511 return wxTreeItemId();
513 wxDir parentDir( parentTreeItem->
GetDir() );
514 std::vector<wxString> projects =
getProjects( parentDir );
517 return wxTreeItemId();
522 wxTreeItemIdValue cookie;
523 wxTreeItemId kid =
m_TreeProject->GetFirstChild( aParent, cookie );
530 return itemData->GetId();
551 if( fname.GetName().CmpNoCase( currfile.GetName() ) == 0 )
557 return wxTreeItemId();
563 return wxTreeItemId();
590 wxTreeItemId newItemId =
m_TreeProject->AppendItem( aParent, file );
597 wxString fileName = currfile.GetName().Lower();
598 wxString projName =
project.GetName().Lower();
600 if( fileName == projName || fileName.StartsWith( projName +
"-" ) )
604 bool subdir_populated =
false;
615 std::vector<wxString> projects =
getProjects( dir );
616 wxString dir_filename;
617 bool haveFile = dir.GetFirst( &dir_filename );
622 subdir_populated = aRecurse;
628 wxString
path = aName + wxFileName::GetPathSeparator() + dir_filename;
631 haveFile = dir.GetNext( &dir_filename );
640 if( subdir_populated )
662 wxString pro_dir =
m_Parent->GetProjectFileName();
680 wxFileName fn = pro_dir;
681 bool prjReset =
false;
692 bool prjOpened = fn.FileExists();
695 if(
Pgm().GetCommonSettings()->m_Git.enableGit
696 && !
Prj().GetLocalSettings().m_GitIntegrationDisabled )
706 const char* canonicalWorkDir = git_repository_workdir(
m_TreeProject->GetGitRepo() );
708 if( canonicalWorkDir )
711 fn.GetPath(), wxString::FromUTF8( canonicalWorkDir ) );
715 m_TreeProject->GitCommon()->SetUsername(
Prj().GetLocalSettings().m_GitRepoUsername );
716 m_TreeProject->GitCommon()->SetSSHKey(
Prj().GetLocalSettings().m_GitSSHKey );
723 if( !prjOpened && !prjReset )
726 prjOpened = fn.FileExists();
746 pro_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
747 wxDir dir( pro_dir );
751 std::vector<wxString> projects =
getProjects( dir );
753 bool haveFile = dir.GetFirst( &filename );
757 if( filename != fn.GetFullName() )
759 wxString
name = dir.GetName() + wxFileName::GetPathSeparator() + filename;
766 haveFile = dir.GetNext( &filename );
783 wxLogTrace(
traceGit,
"PROJECT_TREE_PANE::ReCreateTreePrj: starting timers" );
802 wxTreeItemId curr_item = Event.GetItem();
809 wxFileName prj_dir(
Prj().GetProjectPath(), wxEmptyString );
811 prj_dir.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_CASE | wxPATH_NORM_DOTS
812 | wxPATH_NORM_ENV_VARS | wxPATH_NORM_TILDE );
813 git_dir.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_CASE | wxPATH_NORM_DOTS
814 | wxPATH_NORM_ENV_VARS | wxPATH_NORM_TILDE );
815 wxString prj_name = prj_dir.GetFullPath();
816 wxString git_name = git_dir.GetFullPath();
818 bool can_switch_to_project =
true;
819 bool can_create_new_directory =
true;
820 bool can_open_this_directory =
true;
821 bool can_edit =
true;
822 bool can_rename =
true;
823 bool can_delete =
true;
824 bool run_jobs =
false;
828 bool vcs_can_init = !vcs_has_repo;
831 bool vcs_can_remove = ( vcs_has_repo && git_name.StartsWith( prj_name ) ) || gitIntegrationDisabled;
834 bool vcs_can_pull = vcs_can_fetch;
835 bool vcs_can_switch = vcs_has_repo;
841 vcs_menu &= libgit_init;
843 if( selection.size() == 0 )
847 if( selection.size() != 1 )
849 can_switch_to_project =
false;
850 can_create_new_directory =
false;
859 can_switch_to_project =
false;
865 can_delete = item->CanDelete();
866 can_rename = item->CanRename();
868 switch( item->GetType() )
876 can_switch_to_project =
false;
880 can_create_new_directory =
false;
881 can_open_this_directory =
false;
886 can_switch_to_project =
false;
893 can_switch_to_project =
false;
894 can_create_new_directory =
false;
895 can_open_this_directory =
false;
908 can_switch_to_project =
false;
909 can_create_new_directory =
false;
910 can_open_this_directory =
false;
920 if( can_switch_to_project )
923 _(
"Close all editors, and switch to the selected project" ),
925 popup_menu.AppendSeparator();
928 if( can_create_new_directory )
934 if( can_open_this_directory )
936 if( selection.size() == 1 )
939 text =
_(
"Reveal in Finder" );
940 help_text =
_(
"Reveals the directory in a Finder window" );
942 text =
_(
"Open Directory in File Explorer" );
943 help_text =
_(
"Opens the directory in the default system file manager" );
949 text =
_(
"Reveal in Finder" );
950 help_text =
_(
"Reveals the directories in a Finder window" );
952 text =
_(
"Open Directories in File Explorer" );
953 help_text =
_(
"Opens the directories in the default system file manager" );
963 if( selection.size() == 1 )
964 help_text =
_(
"Open the file in a Text Editor" );
966 help_text =
_(
"Open files in a Text Editor" );
972 if( run_jobs && selection.size() == 1 )
980 if( selection.size() == 1 )
982 text =
_(
"Rename File..." );
983 help_text =
_(
"Rename file" );
987 text =
_(
"Rename Files..." );
988 help_text =
_(
"Rename files" );
997 if( selection.size() == 1 )
998 help_text =
_(
"Delete the file and its content" );
1000 help_text =
_(
"Delete the files and their contents" );
1002 if( can_switch_to_project
1003 || can_create_new_directory
1004 || can_open_this_directory
1008 popup_menu.AppendSeparator();
1022 wxMenu* vcs_submenu =
new wxMenu();
1023 wxMenu* branch_submenu =
new wxMenu();
1024 wxMenuItem* vcs_menuitem =
nullptr;
1027 _(
"Add Project to Version Control..." ),
1028 _(
"Initialize a new repository" ) );
1029 vcs_menuitem->Enable( vcs_can_init );
1033 _(
"Commit changes to the local repository" ) );
1034 vcs_menuitem->Enable( vcs_can_commit );
1036 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PUSH,
_(
"Push" ),
1037 _(
"Push committed local changes to remote repository" ) );
1038 vcs_menuitem->Enable( vcs_can_push );
1040 vcs_menuitem = vcs_submenu->Append(
ID_GIT_PULL,
_(
"Pull" ),
1041 _(
"Pull changes from remote repository into local" ) );
1042 vcs_menuitem->Enable( vcs_can_pull );
1044 vcs_submenu->AppendSeparator();
1047 _(
"Commit changes to the local repository" ) );
1048 vcs_menuitem->Enable( vcs_can_commit );
1050 vcs_submenu->AppendSeparator();
1056 std::vector<wxString> branchNames =
m_TreeProject->GitCommon()->GetBranchNames();
1059 for(
size_t ii = 1; ii < branchNames.size() && ii < 6; ++ii )
1061 wxString msg =
_(
"Switch to branch " ) + branchNames[ii];
1063 vcs_menuitem->Enable( vcs_can_switch );
1067 _(
"Switch to a different branch" ) );
1068 vcs_menuitem->Enable( vcs_can_switch );
1072 vcs_submenu->AppendSeparator();
1074 if( gitIntegrationDisabled )
1076 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Enable Git Integration" ),
1077 _(
"Re-enable Git integration for this project" ) );
1081 vcs_menuitem = vcs_submenu->Append(
ID_GIT_REMOVE_VCS,
_(
"Disable Git Integration" ),
1082 _(
"Disable Git integration for this project" ) );
1085 vcs_menuitem->Enable( vcs_can_remove );
1087 popup_menu.AppendSeparator();
1088 popup_menu.AppendSubMenu( vcs_submenu,
_(
"Version Control" ) );
1091 if( popup_menu.GetMenuItemCount() > 0 )
1092 PopupMenu( &popup_menu );
1100 if( editorname.IsEmpty() )
1102 wxMessageBox(
_(
"No text editor selected in KiCad. Please choose one." ) );
1110 wxString fullFileName = item_data->GetFileName();
1112 if( !fullFileName.IsEmpty() )
1114 ExecuteFile( editorname, fullFileName.wc_str(),
nullptr,
false );
1125 item_data->Delete();
1135 if( tree_data.size() != 1 )
1139 wxString msg = wxString::Format(
_(
"Change filename: '%s'" ),
1140 tree_data[0]->GetFileName() );
1141 wxTextEntryDialog dlg( wxGetTopLevelParent(
this ), msg,
_(
"Change filename" ), buffer );
1143 if( dlg.ShowModal() != wxID_OK )
1146 buffer = dlg.GetValue();
1147 buffer.Trim(
true );
1148 buffer.Trim(
false );
1150 if( buffer.IsEmpty() )
1153 tree_data[0]->Rename( buffer,
true );
1162 if( tree_data.size() != 1 )
1185 std::vector<wxTreeItemId> validItemIds;
1188 for( wxTreeItemId
id : validItemIds )
1207 wxTreeItemId itemId = Event.GetItem();
1217 wxTreeItemIdValue cookie;
1218 wxTreeItemId kid =
m_TreeProject->GetFirstChild( itemId, cookie );
1221 bool subdir_populated =
false;
1224 for( ; kid.IsOk(); kid =
m_TreeProject->GetNextChild( itemId, cookie ) )
1235 wxDir dir( fileName );
1237 if( dir.IsOpened() )
1239 std::vector<wxString> projects =
getProjects( dir );
1240 wxString dir_filename;
1241 bool haveFile = dir.GetFirst( &dir_filename );
1246 wxString
name = fileName + wxFileName::GetPathSeparator() + dir_filename;
1249 haveFile = dir.GetNext( &dir_filename );
1255 subdir_populated =
true;
1264 if( subdir_populated )
1272 wxArrayTreeItemIds selection;
1273 std::vector<PROJECT_TREE_ITEM*> data;
1277 for(
const wxTreeItemId itemId : selection )
1283 wxLogTrace(
traceGit, wxS(
"Null tree item returned for selection, dynamic_cast failed?" ) );
1287 data.push_back( item );
1302 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
1306 if( prj_dir == aSubDir )
1310 wxTreeItemIdValue cookie;
1311 wxTreeItemId root_id =
m_root;
1312 std::stack<wxTreeItemId> subdirs_id;
1314 wxTreeItemId child =
m_TreeProject->GetFirstChild( root_id, cookie );
1318 if( ! child.IsOk() )
1320 if( subdirs_id.empty() )
1327 root_id = subdirs_id.top();
1348 subdirs_id.push( child );
1366 if( !( event.GetChangeType() & ( wxFSW_EVENT_CREATE |
1367 wxFSW_EVENT_DELETE |
1368 wxFSW_EVENT_RENAME |
1369 wxFSW_EVENT_MODIFY ) ) )
1374 const wxFileName& pathModified =
event.GetPath();
1377 if( pathModified.GetFullPath().Contains( wxS(
".history" ) ) )
1380 wxString subdir = pathModified.GetPath();
1381 wxString fn = pathModified.GetFullPath();
1384 if( pathModified.GetFullName().IsEmpty() )
1386 subdir = subdir.BeforeLast(
'/' );
1387 fn = fn.BeforeLast(
'/' );
1392 if( !root_id.IsOk() )
1395 CallAfter( [
this] ()
1397 wxLogTrace(
traceGit, wxS(
"File system event detected, updating tree cache" ) );
1401 wxTreeItemIdValue cookie;
1402 wxTreeItemId kid =
m_TreeProject->GetFirstChild( root_id, cookie );
1404 switch( event.GetChangeType() )
1406 case wxFSW_EVENT_CREATE:
1421 case wxFSW_EVENT_DELETE:
1435 case wxFSW_EVENT_RENAME :
1437 const wxFileName& newpath =
event.GetNewPath();
1438 wxString newdir = newpath.GetPath();
1439 wxString newfn = newpath.GetFullPath();
1463 if( rootData && newpath.Exists() && ( newfn != rootData->
GetFileName() ) )
1469 if( newitem.IsOk() )
1490 wxString prj_dir = wxPathOnly(
m_Parent->GetProjectFileName() );
1492#if defined( _WIN32 )
1501 m_Parent->m_FileWatcherInfo =
_(
"Network path: not monitoring folder changes" );
1507 m_Parent->m_FileWatcherInfo =
_(
"Local path: monitoring folder changes" );
1525 class WatcherLogHandler :
public wxLog
1528 explicit WatcherLogHandler(
bool* err ) :
1536 void DoLogTextAtLevel( wxLogLevel level,
const wxString&
text )
override
1538 if( m_err && ( level == wxLOG_Error || level == wxLOG_FatalError ) )
1546 bool watcherHasError =
false;
1547 WatcherLogHandler tmpLog( &watcherHasError );
1548 wxLog* oldLog = wxLog::SetActiveTarget( &tmpLog );
1554 wxLog::SetActiveTarget( oldLog );
1556 if( watcherHasError )
1564 fn.AssignDir( prj_dir );
1565 fn.DontFollowLink();
1579 TO_UTF8( fn.GetFullPath() ) );
1587 TO_UTF8( fn.GetFullPath() ) );
1592 if( m_TreeProject->IsEmpty() )
1596 wxTreeItemIdValue cookie;
1597 wxTreeItemId root_id = m_root;
1599 std::stack < wxTreeItemId > subdirs_id;
1601 wxTreeItemId kid = m_TreeProject->GetFirstChild( root_id, cookie );
1602 int total_watch_count = 0;
1608 if( subdirs_id.empty() )
1614 root_id = subdirs_id.top();
1616 kid = m_TreeProject->GetFirstChild( root_id, cookie );
1631 if(
path.Contains( wxS(
".history" ) ) )
1633 kid = m_TreeProject->GetNextChild( root_id, cookie );
1639 if( wxFileName::IsDirReadable(
path ) )
1644 fn.AssignDir(
path );
1645 m_watcher->Add( fn );
1646 total_watch_count++;
1650 if( itemData->
IsPopulated() && m_TreeProject->GetChildrenCount( kid ) )
1651 subdirs_id.push( kid );
1655 kid = m_TreeProject->GetNextChild( root_id, cookie );
1662#if defined(DEBUG) && 1
1663 wxArrayString paths;
1664 m_watcher->GetWatchedPaths( &paths );
1667 for(
unsigned ii = 0; ii < paths.GetCount(); ii++ )
1685 std::unique_lock<std::mutex> lock( common->
m_gitActionMutex, std::try_to_lock );
1687 constexpr auto kGraceMs = std::chrono::seconds( 2 );
1688 auto graceEnd = std::chrono::steady_clock::now() + kGraceMs;
1690 while( !lock.owns_lock() && std::chrono::steady_clock::now() < graceEnd )
1692 if( lock.try_lock() )
1694 std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
1697 constexpr auto kCheckInterval = std::chrono::seconds( 30 );
1698 bool userAbandoned =
false;
1700 while( !lock.owns_lock() && !userAbandoned )
1702 auto intervalEnd = std::chrono::steady_clock::now() + kCheckInterval;
1705 wxProgressDialog progress(
_(
"Please wait" ),
1706 _(
"Closing project..." ),
1708 wxPD_APP_MODAL | wxPD_SMOOTH );
1710 while( !lock.try_lock()
1711 && std::chrono::steady_clock::now() < intervalEnd )
1714 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
1719 if( lock.owns_lock() )
1722 wxMessageDialog ask(
this,
1723 _(
"A Git operation is still running.\n"
1724 "Keep waiting, or abandon?" ),
1725 _(
"Git Operation Delayed" ),
1726 wxYES_NO | wxICON_QUESTION );
1727 ask.SetYesNoLabels(
_(
"Keep Waiting" ),
_(
"Abandon" ) );
1729 if( ask.ShowModal() == wxID_NO )
1730 userAbandoned =
true;
1736 std::unique_ptr<KIGIT_COMMON> oldCommon =
m_TreeProject->TakeGitCommon();
1746 wxString projectDir = oldCommon ? oldCommon->GetProjectDir() : wxString();
1748 auto cleanup = [orphan, old = std::move( oldCommon )]()
mutable
1750 std::lock_guard<std::mutex> g( old->m_gitActionMutex );
1751 git_repository_free( orphan );
1758 std::string label =
"abandon close " + projectDir.ToStdString();
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() );
1816 wxString dir = tree_data->
GetDir();
1820 wxLogError(
"Failed to initialize git project: project directory is empty." );
1825 wxWindow* topLevelParent = wxGetTopLevelParent(
this );
1830 _(
"The selected directory is already a Git project." ) );
1845 dlg.SetTitle(
_(
"Set default remote" ) );
1900 handler.
SetProgressReporter( std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetch Remote" ), 1,
1923 handler.
SetProgressReporter( std::make_unique<WX_PROGRESS_REPORTER>(
this,
_(
"Fetch Remote" ), 1,
1943 wxString branchName;
1952 if( retval == wxID_ADD )
1954 else if( retval != wxID_OK )
1959 std::vector<wxString> branches =
m_TreeProject->GitCommon()->GetBranchNames();
1962 if( branchIndex < 0 ||
static_cast<size_t>( branchIndex ) >= branches.size() )
1965 branchName = branches[branchIndex];
1968 wxLogTrace(
traceGit, wxS(
"onGitSwitchBranch: Switching to branch '%s'" ), branchName );
1983 wxLogTrace(
traceGit, wxS(
"onGitRemoveVCS: Git integration %s" ),
1993 std::stack<wxTreeItemId> items;
1996 while( !items.empty() )
1998 wxTreeItemId current = items.top();
2001 m_TreeProject->SetItemState( current, wxTREE_ITEMSTATE_NONE );
2003 wxTreeItemIdValue cookie;
2004 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
2006 while( child.IsOk() )
2008 items.push( child );
2016 wxFileName fn(
Prj().GetProjectPath() );
2023 const char* canonicalWorkDir = git_repository_workdir(
m_TreeProject->GetGitRepo() );
2025 if( canonicalWorkDir )
2028 fn.GetPath(), wxString::FromUTF8( canonicalWorkDir ) );
2029 m_TreeProject->GitCommon()->SetProjectDir( symlinkWorkDir );
2044 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Updating git status icons" ) );
2047 if( !lock.owns_lock() )
2049 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Failed to acquire lock for git status icon update" ) );
2056 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Git is disabled or tree control is null" ) );
2060 std::stack<wxTreeItemId> items;
2063 while( !items.empty() )
2065 wxTreeItemId current = items.top();
2070 wxTreeItemIdValue cookie;
2071 wxTreeItemId child =
m_TreeProject->GetFirstChild( current, cookie );
2073 while( child.IsOk() )
2075 items.push( child );
2079 m_TreeProject->SetItemState( child,
static_cast<int>( it->second ) );
2094 wxString filename = wxFileNameFromPath( rootItem->
GetFileName() );
2100 wxLogTrace(
traceGit, wxS(
"updateGitStatusIcons: Git status icons updated" ) );
2106 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Updating tree cache" ) );
2110 if( !lock.owns_lock() )
2112 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Failed to acquire lock for tree cache update" ) );
2118 wxLogTrace(
traceGit, wxS(
"updateTreeCache: Tree control is null" ) );
2129 std::stack<wxTreeItemId> items;
2132 while( !items.empty() )
2144 gitAbsPath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2148 wxTreeItemIdValue cookie;
2149 wxTreeItemId child =
m_TreeProject->GetFirstChild( kid, cookie );
2151 while( child.IsOk() )
2153 items.push( child );
2162 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Updating git status icons" ) );
2163#if defined( _WIN32 )
2186 if( !lock1.owns_lock() || !lock2.owns_lock() )
2188 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Failed to acquire locks for git status icon update" ) );
2194 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: No git repository found" ) );
2200 std::unique_lock<std::mutex> gitLock(
m_TreeProject->GitCommon()->m_gitActionMutex, std::try_to_lock );
2202 if( !gitLock.owns_lock() )
2204 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Failed to acquire git action mutex" ) );
2211 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Cancelled" ) );
2218 wxFileName rootFilename(
Prj().GetProjectFullName() );
2221 wxFileName relative = rootFilename;
2222 relative.MakeRelativeTo( repoWorkDir );
2223 wxString pathspecStr = relative.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
2226 pathspecStr.Replace( wxS(
"\\" ), wxS(
"/" ) );
2230 auto fileStatusMap = statusHandler.
GetFileStatus( pathspecStr );
2231 auto [localChanges, remoteChanges] =
m_TreeProject->GitCommon()->GetDifferentFiles();
2234 bool updated =
false;
2237 for(
const auto& [absPath, fileStatus] : fileStatusMap )
2242 wxLogTrace(
traceGit, wxS(
"File '%s' not found in tree cache" ), absPath );
2246 auto [it, inserted] =
m_gitStatusIcons.try_emplace( iter->second, fileStatus.status );
2247 if( inserted || it->second != fileStatus.status )
2249 it->second = fileStatus.status;
2255 wxLogTrace(
traceGit, wxS(
"updateGitStatusIconMap: Updated git status icons" ) );
2275 if( repo ==
nullptr )
2277 wxMessageBox(
_(
"The selected directory is not a Git project." ) );
2289 std::map<wxString, int> modifiedFiles;
2290 std::set<wxString> selected_files;
2295 selected_files.emplace( item->GetFileName() );
2300 for(
const auto& [absPath, fileStatus] : fileStatusMap )
2310 wxFileName fn( absPath );
2313 wxString relativePath = absPath;
2314 if( relativePath.StartsWith( repoWorkDir ) )
2316 relativePath = relativePath.Mid( repoWorkDir.length() );
2318 relativePath.Replace( wxS(
"\\" ), wxS(
"/" ) );
2324 if( !absPath.StartsWith( projectPath ) )
2339 if( fn.GetPath().Contains(
Prj().GetProjectName() + wxT(
"-backups" ) ) )
2344 modifiedFiles.emplace( relativePath, fileStatus.gitStatus );
2346 else if( selected_files.count( absPath ) )
2348 modifiedFiles.emplace( relativePath, fileStatus.gitStatus );
2357 if( ret != wxID_OK )
2364 wxMessageBox(
_(
"Discarding commit due to empty commit message." ) );
2370 wxMessageBox(
_(
"Discarding commit due to empty file selection." ) );
2380 wxMessageBox( wxString::Format(
_(
"Failed to create commit: %s" ),
2385 wxLogTrace(
traceGit, wxS(
"Created commit" ) );
2405 for(
const auto& [filePath, fileStatus] : fileStatusMap )
2407 if( filePath.EndsWith( aFile ) || filePath == aFile )
2421 wxLogTrace(
traceGit,
"Syncing project" );
2426 wxLogTrace(
traceGit,
"sync: No git repository found" );
2493 wxLogTrace(
traceGit,
"onGitSyncTimer" );
2507 wxLogTrace(
traceGit,
"onGitSyncTimer: No git repository found" );
2514 wxLogTrace(
traceGit,
"onGitSyncTimer: Cancelled" );
2528 wxLogTrace(
traceGit,
"onGitSyncTimer: Restarting git sync timer" );
2551 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 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
wxString GetGitRootDirectory() const
bool HasPushAndPullRemote() const
bool HasLocalCommits() const
void SetCancelled(bool aCancel)
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[]
#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.