29#include <wx/dcgraph.h>
30#include <wx/dcmemory.h>
33#include <wx/mstream.h>
34#include <wx/richmsgdlg.h>
59wxColour avatarColour(
const wxString& aName )
61 static const wxColour palette[] = {
62 wxColour( 0x3b, 0x7d, 0xd8 ), wxColour( 0x8a, 0x5c, 0xd0 ), wxColour( 0x2f, 0x9e, 0x52 ),
63 wxColour( 0xc0, 0x85, 0x1a ), wxColour( 0xcf, 0x3b, 0x3b ), wxColour( 0x0e, 0x9a, 0xa0 ),
68 for( wxUniChar c : aName )
69 hash = hash * 31 +
static_cast<unsigned>( c.GetValue() );
71 return palette[hash % (
sizeof( palette ) /
sizeof( palette[0] ) )];
75wxString initialsOf(
const wxString& aName )
79 for(
const wxString& part : wxSplit( aName,
' ' ) )
83 result += wxString( part[0] ).Upper();
96wxBitmap makeAvatarBitmap( wxWindow* aCtx,
const wxString& aName,
int aSlot,
int aDiameter )
98 wxBitmap bmp( aSlot, aSlot, 32 );
101 wxMemoryDC mdc( bmp );
104 dc.SetBackground( *wxTRANSPARENT_BRUSH );
107 int offset = ( aSlot - aDiameter ) / 2;
109 dc.SetPen( *wxTRANSPARENT_PEN );
110 dc.SetBrush( wxBrush( avatarColour( aName ) ) );
111 dc.DrawEllipse( offset, offset, aDiameter, aDiameter );
113 wxFont font = aCtx->GetFont();
114 font.SetWeight( wxFONTWEIGHT_BOLD );
115 font.SetPixelSize( wxSize( 0, aDiameter / 2 ) );
117 dc.SetTextForeground( *wxWHITE );
119 wxString ini = initialsOf( aName );
120 wxSize te = dc.GetTextExtent( ini );
121 dc.DrawText( ini, ( aSlot - te.x ) / 2, ( aSlot - te.y ) / 2 );
123 mdc.SelectObject( wxNullBitmap );
131std::string fetchGravatarBytes(
const wxString& aEmail,
int aPixels,
const std::atomic<bool>& aCancel )
137 std::string hash = picosha2::hash256_hex_string( std::string(
normalized.utf8_string() ) );
140 curl.
SetURL(
"https://www.gravatar.com/avatar/" + hash +
"?s=" + std::to_string( aPixels ) +
"&d=404" );
151 [&aCancel](
size_t,
size_t,
size_t,
size_t ) ->
int
153 return aCancel ? 1 : 0;
166wxBitmap makeCircularBitmap(
const wxImage& aImage,
int aDiameter )
168 wxImage scaled = aImage.Scale( aDiameter, aDiameter, wxIMAGE_QUALITY_HIGH );
170 if( !scaled.HasAlpha() )
175 const double centre = ( aDiameter - 1 ) / 2.0;
176 const double radius = aDiameter / 2.0;
178 for(
int y = 0; y < aDiameter; ++y )
180 for(
int x = 0; x < aDiameter; ++x )
182 double dx = x - centre;
183 double dy = y - centre;
186 scaled.SetAlpha( x, y, 0 );
190 return wxBitmap( scaled );
196class HISTORY_AVATAR_RENDERER :
public wxDataViewCustomRenderer
199 HISTORY_AVATAR_RENDERER(
int aRowHeight,
int aGap ) :
200 wxDataViewCustomRenderer( wxT(
"wxDataViewIconText" ), wxDATAVIEW_CELL_INERT ),
201 m_rowHeight( aRowHeight ),
206 bool SetValue(
const wxVariant& aValue )
override
212 bool GetValue( wxVariant& aValue )
const override
218 wxSize GetSize()
const override {
return wxSize( GetOwner()->GetWidth(), m_rowHeight ); }
220 bool Render( wxRect aRect, wxDC* aDc,
int aState )
override
222 RenderBackground( aDc, aRect );
224 wxBitmap bmp = m_value.GetBitmapBundle().GetBitmap( wxDefaultSize );
229 int y = aRect.y + ( aRect.height - bmp.GetHeight() ) / 2;
230 aDc->DrawBitmap( bmp, aRect.x, y,
true );
231 textX += bmp.GetWidth() + m_gap;
234 wxRect textRect( textX, aRect.y, aRect.GetRight() - textX, aRect.height );
235 RenderText( m_value.GetText(), 0, textRect, aDc, aState );
240 wxDataViewIconText m_value;
246wxString joinNames(
const std::vector<wxString>& aNames )
250 for(
const wxString&
name : aNames )
251 joined += ( joined.IsEmpty() ? wxString() : wxString( wxS(
", " ) ) ) +
name;
266 default:
return _(
"modified" );
286 m_historySearch->SetDescriptiveText(
_(
"Search history" ) );
287 m_historyPathRow->ShowItems(
false );
291 int rowHeight = m_historyList->FromDIP( 28 );
292 int gap = m_historyList->FromDIP( 6 );
295 m_historyList->SetRowHeight( rowHeight );
297 m_historyList->AppendColumn(
298 new wxDataViewColumn(
_(
"Description" ),
new HISTORY_AVATAR_RENDERER( rowHeight, gap ), 0,
299 m_historyList->FromDIP( 320 ), wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE ),
300 wxT(
"wxDataViewIconText" ) );
301 m_historyList->AppendTextColumn(
_(
"Author" ), wxDATAVIEW_CELL_INERT, m_historyList->FromDIP( 120 ) );
302 m_historyList->AppendTextColumn(
_(
"Date" ), wxDATAVIEW_CELL_INERT, m_historyList->FromDIP( 90 ) );
303 m_historyList->AppendTextColumn(
_(
"Commit" ), wxDATAVIEW_CELL_INERT, m_historyList->FromDIP( 90 ) );
304 m_historyRefsColumn =
305 m_historyList->AppendTextColumn(
_(
"Refs" ), wxDATAVIEW_CELL_INERT, m_historyList->FromDIP( 120 ) );
307 m_commitFiles->AppendColumn(
_(
"File" ), wxLIST_FORMAT_LEFT, FromDIP( 220 ) );
308 m_commitFiles->AppendColumn(
_(
"Status" ), wxLIST_FORMAT_LEFT, FromDIP( 90 ) );
309 m_commitFiles->AppendColumn( wxS(
"+" ), wxLIST_FORMAT_RIGHT, FromDIP( 50 ) );
310 m_commitFiles->AppendColumn( wxS(
"-" ), wxLIST_FORMAT_RIGHT, FromDIP( 50 ) );
314 m_commitAdded->SetForegroundColour( theme.
added.
ToColour() );
318 m_historySashPos = FromDIP( 460 );
319 m_historySplitter->Unsplit( m_commitDetail );
320 m_detailSashPos = FromDIP( 140 );
324 m_historyScrollTimer.SetOwner(
this );
327 [
this]( wxTimerEvent& )
329 onHistoryScrollTick();
331 m_historyScrollTimer.GetId() );
333 m_showAllButton->Bind( wxEVT_BUTTON,
334 [
this]( wxCommandEvent& )
336 ShowHistoryForFile( wxEmptyString );
341 m_historySearch->Bind( wxEVT_TEXT,
342 [
this]( wxCommandEvent& )
346 m_historyQuery.Cancel();
347 m_detailQuery.Cancel();
348 m_historyScrollTimer.Stop();
350 m_historySearchDebounce.Restart();
353 m_historyBranch->Bind( wxEVT_CHOICE,
354 [
this]( wxCommandEvent& )
356 int sel = m_historyBranch->GetSelection();
357 m_historyBranchName = sel > 0 ? m_historyBranch->GetString( sel ) : wxString();
361 m_historyShowRefs->Bind( wxEVT_CHECKBOX,
362 [
this]( wxCommandEvent& )
364 m_historyRefsColumn->SetHidden( !m_historyShowRefs->IsChecked() );
367 m_historyList->Bind( wxEVT_DATAVIEW_SELECTION_CHANGED,
368 [
this]( wxDataViewEvent& )
370 if( m_fillingHistory )
373 int row = m_historyList->GetSelectedRow();
374 wxString hash = row == wxNOT_FOUND ? wxString() : m_historyList->GetTextValue( row, 3 );
375 showCommitDetail( hash );
379 m_historyList->SetToolTip(
_(
"Double click a commit to see what it changed. Right click for more." ) );
380 m_historyList->Bind( wxEVT_DATAVIEW_ITEM_ACTIVATED,
381 [
this]( wxDataViewEvent& aEvent )
383 int row = m_historyList->ItemToRow( aEvent.GetItem() );
385 if( row == wxNOT_FOUND )
388 wxString hash = m_historyList->GetTextValue( row, 3 );
390 if( !hash.IsEmpty() )
395 m_historyList->Bind( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU,
396 [
this]( wxDataViewEvent& aEvent )
398 int row = m_historyList->ItemToRow( aEvent.GetItem() );
400 if( row == wxNOT_FOUND )
403 wxString hash = m_historyList->GetTextValue( row, 3 );
413 if( row <
static_cast<int>( m_historyAtHead.size() ) && m_historyAtHead[row] )
414 menu.Append( 1,
_(
"Amend Last Commit..." ) );
416 menu.Append( 2,
_(
"Restore This Version" ) );
420 [
this, hash]( wxCommandEvent& )
422 m_dialog->ShowAmend( hash );
427 [
this, hash]( wxCommandEvent& )
429 onRestoreVersion( hash );
432 m_historyList->PopupMenu( &menu );
436 m_commitFiles->Bind( wxEVT_LIST_ITEM_SELECTED,
437 [
this]( wxListEvent& aEvent )
439 if( !m_fillingHistory )
440 showFileDiff( m_commitFiles->GetItemText( aEvent.GetIndex(), 0 ) );
443 m_commitFiles->Bind( wxEVT_LIST_ITEM_RIGHT_CLICK,
444 [
this]( wxListEvent& aEvent )
448 wxString
path = m_commitFiles->GetItemText( aEvent.GetIndex(), 0 );
454 menu.Append( 1,
_(
"Show History for This File" ) );
456 if( GetPopupMenuSelectionFromUser( menu ) == 1 )
457 ShowHistoryForFile(
path );
461 m_commitFiles->SetToolTip(
_(
"Double click a file to see what the commit changed in it." ) );
462 m_commitFiles->Bind( wxEVT_LIST_ITEM_ACTIVATED,
463 [
this]( wxListEvent& aEvent )
466 m_commitFiles->GetItemText( aEvent.GetIndex(), 0 ) );
547 if( aHash.IsEmpty() )
553 if( !
m_dialog->SessionStillCurrent() )
556 auto detail = std::make_shared<KIGIT_COMMIT_DETAIL>();
562 *detail = aCommon.GetCommitDetail( hash );
566 if( m_dialog->SessionStillCurrent() )
567 fillCommitDetail( *detail );
582 for(
const wxString& ref : aDetail.
refs )
583 title += wxS(
" [" ) + ref + wxS(
"]" );
596 if( showAvatars && cached !=
m_avatarCache.end() && cached->second.IsOk() )
606 wxString parents = aDetail.
parents.empty() ?
_(
"No parent" )
607 : wxString::Format(
_(
"Parent: %s" ), joinNames( aDetail.
parents ) );
612 int fileCount =
static_cast<int>( aDetail.
files.size() );
613 wxString files = fileCount == 1 ?
_(
"1 changed file" ) : wxString::Format(
_(
"%d changed files" ), fileCount );
626 wxString status = fileStatusLabel( file.
status );
629 status = wxString::Format(
_(
"%s from %s" ), status, file.
oldPath );
649 if( row != wxNOT_FOUND )
652 m_commitFiles->SetItemState( row, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
697 wxString beforeLabel =
699 wxString afterLabel = wxString::Format(
_(
"WORKING (%s)" ),
m_detailHash );
706 return MaterializeCommitFile( aCommon, aWorkspace, hash, path, aSides );
708 beforeLabel, afterLabel );
721 if( !
m_dialog->SessionStillCurrent() )
724 auto commits = std::make_shared<std::vector<KIGIT_COMMIT_INFO>>();
733 [
this, commits, limit]()
735 if(
m_dialog->SessionStillCurrent() )
759 for(
int row = 0; row <
m_historyList->GetItemCount() && !shown.IsEmpty(); ++row )
769 if( selected.IsEmpty() &&
m_historyList->GetItemCount() > 0 )
795 for(
size_t ii = aFrom; ii < aCommits.size(); ++ii )
802 if( showAvatars && cached !=
m_avatarCache.end() && cached->second.IsOk() )
803 avatar = cached->second;
808 iconText << wxDataViewIconText( c.
summary, wxBitmapBundle::FromBitmap( avatar ) );
810 wxVector<wxVariant> row;
811 row.push_back( iconText );
812 row.push_back( wxVariant( c.
author ) );
814 row.push_back( wxVariant( c.
hash ) );
815 row.push_back( wxVariant( joinNames( c.
refs ) ) );
830 || !
m_dialog->SessionStillCurrent() )
841 wxDataViewColumn* column =
nullptr;
844 m_historyList->HitTest( wxPoint( 8, client.y - 8 ), item, column );
846 int row = item.IsOk() ?
m_historyList->ItemToRow( item ) : wxNOT_FOUND;
849 if( row != wxNOT_FOUND && row < itemCount - 5 )
854 auto commits = std::make_shared<std::vector<KIGIT_COMMIT_INFO>>();
863 [
this, commits, limit, itemCount]()
865 if( !
m_dialog->SessionStillCurrent() )
885 const size_t maxAvatarFetches = 24;
892 wxString email = aEmail;
893 int fetchPx = aDiameter * 2;
898 [
this, email, aDiameter, fetchPx]()
900 std::string bytes = fetchGravatarBytes( email, fetchPx,
m_avatarCancel );
903 [
this, email, aDiameter, bytes]()
916 wxMemoryInputStream stream( aBytes.data(), aBytes.size() );
919 if( !
image.LoadFile( stream, wxBITMAP_TYPE_ANY ) )
922 wxBitmap
circle = makeCircularBitmap(
image, aDiameter );
936 wxDataViewIconText iconText;
939 wxDataViewIconText updated( iconText.GetText(), wxBitmapBundle::FromBitmap(
circle ) );
940 wxVariant updatedValue;
941 updatedValue << updated;
949 if( !
m_dialog->SessionStillCurrent() )
952 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
958 bool detached =
false;
962 auto info = std::make_shared<RESTORE_INFO>();
973 [
this, common,
info, aCommitHash]()
975 if( !
info->loaded ||
m_frame->Prj().GetGitCommon() != common || !
m_dialog->SessionStillCurrent() )
981 if( !
info->lfsMessage.IsEmpty() )
990 DisplayInfoMessage(
this,
_(
"This project is showing an old snapshot rather than a branch. "
991 "Double click a branch on the Branches page first, then restore." ) );
1002 std::shared_ptr<KIGIT_COMMON> common =
m_frame->Prj().GetGitCommon();
1005 auto editorsReady = [
this]()
1009 DisplayInfoMessage(
this,
_(
"There are unsaved changes in the open editors. Commit them "
1010 "first, so the restore stays a separate step that can be "
1017 DisplayInfoMessage(
this,
_(
"A library editor has unsaved changes. Save or discard them first, so "
1018 "the restore does not overwrite what it is editing." ) );
1025 if( !editorsReady() )
1028 wxRichMessageDialog confirm(
this,
1029 wxString::Format(
_(
"Restore this project's files as they were at version %s?\n\n"
1030 "They are replaced with that version, recorded as a new "
1031 "commit, so nothing in the history is lost. Files outside "
1032 "the project folder, including any shared with other "
1033 "projects, keep their current state." ),
1035 _(
"Restore Version" ), wxYES_NO | wxICON_QUESTION | wxCENTER );
1036 confirm.SetYesNoLabels(
_(
"&Restore" ),
_(
"Cancel" ) );
1038 if( confirm.ShowModal() != wxID_YES )
1045 if(
m_frame->Prj().GetGitCommon() != common || !
m_dialog->SessionStillCurrent() )
1048 if( !editorsReady() )
1060 if( !lock.owns_lock() )
1063 result = common->RestoreVersion( aCommitHash, error );
1070 common->NotifyRefreshListeners();
1080 auto outside = std::make_shared<std::vector<wxString>>();
1086 *outside = ChangedTrackedFilesOutsideProject( aCommon, path );
1088 [
this, common, outside]()
1090 if( m_frame->Prj().GetGitCommon() != common || !m_dialog->SessionStillCurrent() )
1093 if( !outside->empty() )
1097 for( const wxString& path : *outside )
1098 listed += wxS(
"\n " ) + path;
1103 _(
"There are uncommitted changes elsewhere in this repository:%s\n\n"
1104 "Commit or discard them from that project first, or set them aside with "
1110 DisplayInfoMessage( this, _(
"There are uncommitted changes. Commit them first, so the "
1111 "restore stays a separate step that can be undone." ) );
static std::optional< VECTOR2D > normalized(const VECTOR2D &aVec)
The base frame for deriving all KiCad main window classes.
The advanced Git window: a non-modal dialog with a top bar naming the repository and branch and a sid...
A private scratch directory for extracting committed file versions to diff, removed when this object ...
Shows what changed in one file: the old and new drawing side by side for a schematic or board,...
int Perform()
Equivalent to curl_easy_perform.
bool SetUserAgent(const std::string &aAgent)
Set the request user agent.
int GetResponseStatusCode()
const std::string & GetBuffer()
Return a reference to the received data buffer.
bool SetTransferCallback(const TRANSFER_CALLBACK &aCallback, size_t aInterval)
bool SetURL(const std::string &aURL)
Set the request URL.
bool SetFollowRedirects(bool aFollow)
Enable the following of HTTP(s) and other redirects, by default curl does not follow redirects.
bool SetConnectTimeout(long aTimeoutSecs)
Set the connection timeout in seconds.
bool SetTimeout(long aTimeoutSecs)
Set the total operation timeout in seconds.
wxColour ToColour() const
static wxString LfsRefusal(KIGIT_COMMON *aCommon)
Return a refusal message for LFS files or an unknown project folder, otherwise empty.
The project's git session.
std::vector< KIGIT_COMMIT_INFO > GetRecentCommits(size_t aMax=50, const KIGIT_HISTORY_FILTER &aFilter={}) const
bool IsHeadDetached() const
True when HEAD points at a commit rather than a branch, as after checking out a tag.
wxStaticText * m_commitAdded
wxChoice * m_historyBranch
wxStaticText * m_commitAuthor
wxStaticText * m_commitStats
wxStaticText * m_commitRemoved
wxListCtrl * m_commitFiles
wxSplitterWindow * m_historySplitter
wxTextCtrl * m_commitMessage
wxDataViewListCtrl * m_historyList
wxStaticBitmap * m_commitAvatar
wxStaticText * m_commitFileCount
PANEL_GIT_HISTORY_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
wxPanel * m_historyListPanel
wxStaticText * m_historyPathLabel
wxStaticText * m_commitTitle
wxSplitterWindow * m_detailSplitter
wxBoxSizer * m_historyPathRow
wxPanel * m_commitFilesPanel
wxSearchCtrl * m_historySearch
WX_DEBOUNCED_ACTION m_historySearchDebounce
Waits for the user to pause typing in the search box before walking the history again.
PANEL_GIT_HISTORY(wxWindow *aParent, GIT_ADVANCED_DIALOG *aDialog, EDA_BASE_FRAME *aFrame)
void onHistoryScrollTick()
Grow the history window when the user has scrolled near its end.
~PANEL_GIT_HISTORY() override
bool m_fillingHistory
True while the list is being refilled, when its selection events are its own.
wxString m_detailEmail
Its author's email, so a web avatar that arrives later lands on the pane too.
wxDataViewColumn * m_historyRefsColumn
void Populate()
Load the repository's recent commits in the background, up to the current window.
wxTimer m_historyScrollTimer
Polls whether the user scrolled near the end of the history, which is the one portable way to notice ...
void requestAvatar(const wxString &aEmail, int aDiameter)
Start a background fetch of the contributor's web avatar for aEmail, if opted in and not already cach...
std::atomic< bool > m_avatarCancel
Set when the page goes, so a fetch still waiting on the network gives up rather than holding the clos...
size_t m_historyLimit
How many commits the page currently shows at most. Grows as the user scrolls.
bool m_historyComplete
True once a load returned fewer commits than asked for, meaning everything is shown.
KIGIT_QUERY m_historyQuery
Listing and selection have separate requests so choosing a commit does not cancel paging.
int m_historySashPos
Where the user left the sash, so the pane comes back at the same width.
std::set< wxString > m_avatarRequested
Emails whose avatar fetch was already started, so each is fetched at most once.
KIGIT_QUERY m_actionQuery
Read action preconditions without delaying history selection or scrolling.
void showFileDiff(const wxString &aPath)
Show what the selected commit did to aPath under the file list, or hide that area.
std::vector< wxString > m_historyEmails
Author email for each history row, so a fetched avatar can find its rows.
void SetBranches(const std::vector< KIGIT_BRANCH_INFO > &aBranches)
Offer every branch in the branch filter, keeping the current choice.
std::vector< std::thread > m_avatarWorkers
The avatar fetches this page started, joined before it goes.
void showCommitDetail(const wxString &aHash)
Fill the detail pane with aHash, or clear it when the hash is empty or gone.
void ShowHistoryForFile(const wxString &aPath)
Narrow the list to the commits that changed aPath, a file or folder relative to the repository root.
GIT_ADVANCED_DIALOG * m_dialog
KIGIT_QUERY m_detailQuery
void onRestoreVersion(const wxString &aCommitHash)
Restore the project as it was at the given commit, recorded as a new commit on top of the current hea...
void applyAvatar(const wxString &aEmail, int aDiameter, const std::string &aBytes)
On the UI thread, decode the fetched avatar bytes and place the photo on every history row for aEmail...
GIT_FILE_DIFF_AREA * m_fileDiff
std::vector< bool > m_historyAtHead
Whether each row's commit is the one HEAD points at.
void restoreVersion(const wxString &aCommitHash, const KIGIT::GIT_IDENTITY &aIdentity)
void fillCommitDetail(const KIGIT_COMMIT_DETAIL &aDetail)
wxString m_detailParent
The first parent of the shown commit, which the diff area's before side names.
void fillHistory(const std::vector< KIGIT_COMMIT_INFO > &aCommits, size_t aLimit)
Apply a loaded history window, keeping the currently selected commit when possible.
void appendHistoryRows(const std::vector< KIGIT_COMMIT_INFO > &aCommits, size_t aFrom)
Append rows for aCommits starting at aFrom, without clearing, so growing the history window keeps the...
std::map< wxString, wxBitmap > m_avatarCache
Fetched web avatars, keyed by author email.
void reloadHistory()
Restart the history at the first window after the search or branch filter changed.
KIGIT_HISTORY_FILTER historyFilter() const
The search text and branch the page is narrowed to.
wxString m_historyPath
The file or folder the history is narrowed to, or empty for all of it.
wxString m_historyBranchName
The branch the list is narrowed to, or empty for every branch.
int m_detailSashPos
Where the user left the sash between the file list and the diff area.
wxString m_detailHash
The commit the detail pane shows, so a refresh can put the selection back on it.
virtual COMMON_SETTINGS * GetCommonSettings() 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.
This file is part of the common library.
Base window classes and related definitions.
void ShowGitCommitDiff(EDA_BASE_FRAME *aFrame, const wxString &aCommitHash, wxWindow *aParentWindow, const wxString &aRelativePath)
Open the diff for what a commit changed, comparing it against its parent.
EDITOR_SNAPSHOT CaptureOpenEditorDocs(KIWAY &aKiway)
Record the on-disk identity of the open documents, before an operation that may rewrite them.
void ReloadOpenEditors(KIWAY &aKiway, const EDITOR_SNAPSHOT &aBefore)
Tell the open board and schematic editors to reload from disk.
std::vector< wxString > LibraryEditorsWithUnsavedChanges(KIWAY &aKiway)
Names of the library editors holding unsaved changes.
bool DocumentEditorsDirty(KIWAY &aKiway)
Unsaved schematic or board edits can be saved for a commit, even with no changes on disk.
@ AlreadyCurrent
The chosen version's content matches the current head.
@ Restored
A new commit carrying the old version's content was created.
@ Error
The restore failed. See the error string.
@ Dirty
Uncommitted changes exist, and restoring would overwrite them.
GIT_IDENTITY ReadGitIdentity(KIGIT_COMMON &aCommon)
Read on a worker while holding the session's action lock.
bool EnsureGitIdentity(wxWindow *aParent, EDA_BASE_FRAME *aFrame, const std::shared_ptr< KIGIT_COMMON > &aCommon, const GIT_IDENTITY &aIdentity)
Open the same editor when the worker snapshot has no usable Git identity.
void UnsplitKeepingSash(wxSplitterWindow *aSplitter, wxWindow *aPane, int &aSashPos)
Close a splitter's second pane, keeping the sash where the user left it for next time.
std::unique_lock< std::mutex > TryLockAction(KIGIT_COMMON &aCommon, wxWindow *aParent)
Try the action lock and show a busy message if it is unavailable.
wxString RelativeDate(const wxDateTime &aWhen)
"just now", "3 hours ago", or the ISO date once it is more than a month old.
PGM_BASE & Pgm()
The global program "get" accessor.
bool showContributorAvatars
What the open editors and the project settings looked like on disk before an operation that may rewri...
The two sides of one file, written out for a comparison.
Diff colors with defaults for light backgrounds.
static DIFF_COLOR_THEME ForText(bool aDarkTheme)
Resolve change colors for UI text. Pass the desktop theme captured on the UI thread.
Repository values and whether Git can use them to record a commit.
std::vector< wxString > parents
Short hashes.
std::vector< KIGIT_COMMIT_FILE > files
std::vector< wxString > refs
As in KIGIT_COMMIT_INFO.
wxString message
Full message, body included.
wxString oldPath
For a rename or copy, where the file came from.
KIGIT::FILE_CHANGE_STATUS status
wxString author
Author name.
wxString email
Author email, used to look up a web avatar.
wxString summary
First line of the commit message.
std::vector< wxString > refs
HEAD, branches and tags at this commit, in that order.
wxString hash
Short commit hash.
wxDateTime when
Commit time.
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.