KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_git_commit.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "dialog_git_commit.h"
21
25#include <pgm_base.h>
28
29#include <algorithm>
30
31#include <wx/button.h>
32#include <wx/checkbox.h>
33#include <wx/listctrl.h>
34#include <wx/sizer.h>
35#include <wx/stattext.h>
36#include <wx/textctrl.h>
37
38
39static constexpr const char* DIALOG_GIT_COMMIT_SETTINGS_KEY = "DIALOG_GIT_COMMIT";
40static constexpr const char* LIST_COLUMN_WIDTHS_KEY = "listctrl_column_widths";
41static constexpr const char* FILENAME_COLUMN_WIDTH_KEY = "filename_col_width";
42static constexpr const char* STATUS_COLUMN_WIDTH_KEY = "status_col_width";
43
44
45DIALOG_GIT_COMMIT::DIALOG_GIT_COMMIT( wxWindow* parent, git_repository* repo,
46 const wxString& defaultAuthorName,
47 const wxString& defaultAuthorEmail,
48 const std::map<wxString, int>& filesToCommit ) :
49 DIALOG_SHIM( parent, wxID_ANY, _( "Commit Changes" ) )
50{
51 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
52
53
54 // List Control for files to commit
55 m_listCtrl = new wxListCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT );
56
57 // Set up columns
58 m_listCtrl->EnableCheckBoxes();
59 m_listCtrl->AppendColumn( _( "Filename" ) );
60 m_listCtrl->AppendColumn( _( "Status" ) );
61
62 // Set column widths
64 int filesColWidth = 200;
65 int statusWidthWidth = 200;
67
68 if( dlgIt != settings->CsInternals().m_dialogControlValues.end() )
69 {
70 auto widths = dlgIt->second.find( LIST_COLUMN_WIDTHS_KEY );
71 if( widths != dlgIt->second.end() && widths->second.is_object() )
72 {
73 filesColWidth = widths->second.value( FILENAME_COLUMN_WIDTH_KEY, 200 );
74 statusWidthWidth = widths->second.value( STATUS_COLUMN_WIDTH_KEY, 200 );
75 }
76 }
77
78 m_listCtrl->SetColumnWidth( 0, FromDIP( filesColWidth ) );
79 m_listCtrl->SetColumnWidth( 1, FromDIP( statusWidthWidth ) );
80
81
82 // Set up image list for icons
83#ifdef __WXMAC__
84 // HiDPI-aware API; will be generally available in wxWidgets 3.4
85 wxVector<wxBitmapBundle> stateImages;
86 stateImages.push_back( wxBitmapBundle() ); // GIT_STATUS_UNTRACKED
87 stateImages.push_back( KiBitmapBundle( BITMAPS::git_good_check ) ); // GIT_STATUS_CURRENT
88 stateImages.push_back( KiBitmapBundle( BITMAPS::git_modified ) ); // GIT_STATUS_MODIFIED
89 stateImages.push_back( KiBitmapBundle( BITMAPS::git_add ) ); // GIT_STATUS_ADDED
90 stateImages.push_back( KiBitmapBundle( BITMAPS::git_delete ) ); // GIT_STATUS_DELETED
91 stateImages.push_back( KiBitmapBundle( BITMAPS::git_out_of_date ) ); // GIT_STATUS_BEHIND
92 stateImages.push_back( KiBitmapBundle( BITMAPS::git_changed_ahead ) ); // GIT_STATUS_AHEAD
93 stateImages.push_back( KiBitmapBundle( BITMAPS::git_conflict ) ); // GIT_STATUS_CONFLICTED
94
95 m_listCtrl->SetNormalImages( stateImages );
96 m_listCtrl->SetSmallImages( stateImages );
97#else
98 wxImageList* imageList = new wxImageList(
99 16, 16, true, static_cast<int>( KIGIT_COMMON::GIT_STATUS::GIT_STATUS_LAST ) );
100
101 imageList->Add( KiBitmap( BITMAPS::git_good_check ) ); // PLACEHOLDER
102 imageList->Add( KiBitmap( BITMAPS::git_good_check ) ); // GIT_STATUS_CURRENT
103 imageList->Add( KiBitmap( BITMAPS::git_modified ) ); // GIT_STATUS_MODIFIED
104 imageList->Add( KiBitmap( BITMAPS::git_add ) ); // GIT_STATUS_ADDED
105 imageList->Add( KiBitmap( BITMAPS::git_delete ) ); // GIT_STATUS_DELETED
106 imageList->Add( KiBitmap( BITMAPS::git_out_of_date ) ); // GIT_STATUS_BEHIND
107 imageList->Add( KiBitmap( BITMAPS::git_changed_ahead ) ); // GIT_STATUS_AHEAD
108 imageList->Add( KiBitmap( BITMAPS::git_conflict ) ); // GIT_STATUS_CONFLICTED
109
110 // Assign the image list to the list control
111 m_listCtrl->SetImageList( imageList, wxIMAGE_LIST_SMALL );
112#endif
113
114 // Populate list control with items
115 for( auto& [filename, status] : filesToCommit )
116 {
117 int i = m_listCtrl->GetItemCount();
118 m_listCtrl->InsertItem( i, filename );
119
120 if( status & ( GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW ) )
121 {
122 m_listCtrl->SetItem( i, 1, _( "New" ) );
123 m_listCtrl->SetItemImage(
124 i, static_cast<int>( KIGIT_COMMON::GIT_STATUS::GIT_STATUS_ADDED ) );
125
126 if( status & ( GIT_STATUS_INDEX_NEW ) )
127 m_listCtrl->CheckItem( i, true );
128 }
129 else if( status & ( GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED ) )
130 {
131 m_listCtrl->SetItem( i, 1, _( "Modified" ) );
132 m_listCtrl->SetItemImage(
133 i, static_cast<int>( KIGIT_COMMON::GIT_STATUS::GIT_STATUS_MODIFIED ) );
134
135 if( status & ( GIT_STATUS_INDEX_MODIFIED ) )
136 m_listCtrl->CheckItem( i, true );
137 }
138 else if( status & ( GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED ) )
139 {
140 m_listCtrl->SetItem( i, 1, _( "Deleted" ) );
141 m_listCtrl->SetItemImage(
142 i, static_cast<int>( KIGIT_COMMON::GIT_STATUS::GIT_STATUS_DELETED ) );
143
144 if( status & ( GIT_STATUS_INDEX_DELETED ) )
145 m_listCtrl->CheckItem( i, true );
146 }
147 else
148 {
149 printf( " Unknown status: %d\n", status );
150 }
151 }
152
153 sizer->Add( m_listCtrl, 1, wxEXPAND | wxALL, 5 );
154
155 // Commit Message Text Control
156 wxStaticText* commitMessageLabel = new wxStaticText( this, wxID_ANY, _( "Commit Message:" ) );
158 new wxTextCtrl( this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE );
159
161
162 sizer->Add( commitMessageLabel, 0, wxALL, 5 );
163 sizer->Add( m_commitMessageTextCtrl, 1, wxEXPAND | wxALL, 5 );
164
165 // Author Name and Email Text Control
166 wxStaticText* authorLabel = new wxStaticText( this, wxID_ANY, _( "Author:" ) );
167 wxString defaultAuthor = defaultAuthorName + " <" + defaultAuthorEmail + ">";
169 new wxTextCtrl( this, wxID_ANY, defaultAuthor, wxDefaultPosition, wxDefaultSize, 0 );
170 sizer->Add( authorLabel, 0, wxALL, 5 );
171 sizer->Add( m_authorTextCtrl, 0, wxEXPAND | wxALL, 5 );
172
173 // OK and Cancel Buttons
174
175 wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
176
177 m_okButton = new wxButton( this, wxID_OK, _( "OK" ) );
178 wxButton* cancelButton = new wxButton( this, wxID_CANCEL, _( "Cancel" ) );
179 buttonSizer->Add( cancelButton, 0, wxALL, 5 );
180 buttonSizer->Add( m_okButton, 0, wxALL, 5 );
181 buttonSizer->Realize();
182
183 sizer->Add( buttonSizer, 0, wxALIGN_RIGHT | wxALL, 5 );
184
185 SetSizerAndFit( sizer );
186
187 SetupStandardButtons( { { wxID_OK, _( "C&ommit" ) } } );
188
189 // Bind events
190 Bind( wxEVT_TEXT, &DIALOG_GIT_COMMIT::OnTextChanged, this, m_commitMessageTextCtrl->GetId() );
191 Bind( wxEVT_LIST_ITEM_CHECKED, &DIALOG_GIT_COMMIT::OnItemChecked, this, m_listCtrl->GetId() );
192 Bind( wxEVT_LIST_ITEM_UNCHECKED, &DIALOG_GIT_COMMIT::OnItemUnchecked, this, m_listCtrl->GetId() );
193
194 // Set the repository and defaults
195 m_repo = repo;
196 m_defaultAuthorName = defaultAuthorName;
197 m_defaultAuthorEmail = defaultAuthorEmail;
198
200}
201
202
204{
205 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
206 std::map<std::string, nlohmann::json>& dlgMap =
208
209 nlohmann::json widths;
210 widths[FILENAME_COLUMN_WIDTH_KEY] = ToDIP( m_listCtrl->GetColumnWidth( 0 ) );
211 widths[STATUS_COLUMN_WIDTH_KEY] = ToDIP( m_listCtrl->GetColumnWidth( 1 ) );
212
213 dlgMap[LIST_COLUMN_WIDTHS_KEY] = widths;
214}
215
217{
218 m_requireFiles = aRequired;
220}
221
222
224{
225 bool hasMessage = !m_commitMessageTextCtrl->GetValue().IsEmpty();
226 bool needFiles = m_requireFiles && GetSelectedFiles().empty();
227
228 m_okButton->Enable( hasMessage && !needFiles );
229
230 if( !hasMessage )
231 m_okButton->SetToolTip( _( "Commit message cannot be empty" ) );
232 else if( needFiles )
233 m_okButton->SetToolTip( _( "Select at least one file to commit" ) );
234 else
235 m_okButton->SetToolTip( wxEmptyString );
236}
237
238
239void DIALOG_GIT_COMMIT::OnTextChanged( wxCommandEvent& aEvent )
240{
242}
243
244
246{
247 return m_commitMessageTextCtrl->GetValue();
248}
249
250
251void DIALOG_GIT_COMMIT::SetCommitMessage( const wxString& aMessage )
252{
253 m_commitMessageTextCtrl->SetValue( aMessage );
254}
255
256
258{
259 wxString authorText = m_authorTextCtrl->GetValue();
260 size_t pos = authorText.find( '<' );
261
262 if( pos != wxString::npos )
263 return authorText.substr( 0, pos ).Trim();
264
265 return wxEmptyString;
266}
267
268
270{
271 wxString authorText = m_authorTextCtrl->GetValue();
272 size_t startPos = authorText.find( '<' );
273 size_t endPos = authorText.find( '>' );
274
275 if( startPos != wxString::npos && endPos != wxString::npos && startPos < endPos )
276 return authorText.substr( startPos + 1, endPos - startPos - 1 ).Trim();
277
278 return wxEmptyString;
279}
280
281
282std::vector<wxString> DIALOG_GIT_COMMIT::GetSelectedFiles() const
283{
284 std::vector<wxString> selectedFiles;
285
286 long item = -1;
287
288 while( ( item = m_listCtrl->GetNextItem( item, wxLIST_NEXT_ALL ) ) != -1 )
289 {
290 if( m_listCtrl->IsItemChecked( item ) )
291 selectedFiles.push_back( m_listCtrl->GetItemText( item ) );
292 }
293
294 return selectedFiles;
295}
296
297
298void DIALOG_GIT_COMMIT::OnItemChecked( wxListEvent& aEvent )
299{
300 long checkedItem = aEvent.GetIndex();
301
302 if( m_listCtrl->GetItemState( checkedItem, wxLIST_STATE_SELECTED ) )
303 {
304 long item = -1;
305
306 while( ( item = m_listCtrl->GetNextItem( item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED ) ) != -1 )
307 {
308 if( item != checkedItem )
309 m_listCtrl->CheckItem( item, true );
310 }
311 }
312
314}
315
316
317void DIALOG_GIT_COMMIT::OnItemUnchecked( wxListEvent& aEvent )
318{
319 long uncheckedItem = aEvent.GetIndex();
320
321 if( m_listCtrl->GetItemState( uncheckedItem, wxLIST_STATE_SELECTED ) )
322 {
323 long item = -1;
324
325 while( ( item = m_listCtrl->GetNextItem( item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED ) ) != -1 )
326 {
327 if( item != uncheckedItem )
328 m_listCtrl->CheckItem( item, false );
329 }
330 }
331
333}
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition bitmap.cpp:100
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
@ git_out_of_date
@ git_changed_ahead
COMMON_SETTINGS_INTERNALS & CsInternals()
wxTextCtrl * m_commitMessageTextCtrl
void SetFileSelectionRequired(bool aRequired)
When false, OK is allowed with no files selected (e.g. amending only the message).
void OnTextChanged(wxCommandEvent &event)
wxString GetCommitMessage() const
wxTextCtrl * m_authorTextCtrl
DIALOG_GIT_COMMIT(wxWindow *parent, git_repository *repo, const wxString &defaultAuthorName, const wxString &defaultAuthorEmail, const std::map< wxString, int > &filesToCommit)
void OnItemChecked(wxListEvent &event)
void SetCommitMessage(const wxString &aMessage)
Pre-fill the commit message.
void OnItemUnchecked(wxListEvent &event)
wxString GetAuthorEmail() const
git_repository * m_repo
std::vector< wxString > GetSelectedFiles() const
void updateOkButton()
Enable the OK button only when there is both a message and at least one selected file.
wxString GetAuthorName() const
wxListCtrl * m_listCtrl
void OptOut(wxWindow *aWindow)
Opt out of control state saving.
void SetupStandardButtons(std::map< int, wxString > aLabels={})
DIALOG_SHIM(wxWindow *aParent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE|wxRESIZE_BORDER, const wxString &name=wxDialogNameStr)
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:562
static constexpr const char * DIALOG_GIT_COMMIT_SETTINGS_KEY
static constexpr const char * FILENAME_COLUMN_WIDTH_KEY
static constexpr const char * LIST_COLUMN_WIDTHS_KEY
static constexpr const char * STATUS_COLUMN_WIDTH_KEY
#define _(s)
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
std::map< std::string, std::map< std::string, nlohmann::json > > m_dialogControlValues