KiCad PCB EDA Suite
Loading...
Searching...
No Matches
local_history_pane.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, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include "local_history_pane.h"
25#include "kicad_manager_frame.h"
26
27#include <git2.h>
28#include <project.h>
29#include <wx/filename.h>
30#include <wx/intl.h>
31#include <wx/menu.h>
32
33wxDEFINE_EVENT( EVT_LOCAL_HISTORY_REFRESH, wxCommandEvent );
34
36 m_frame( aParent ), m_list( nullptr ), m_timer( this ), m_refreshTimer( this ),
37 m_hoverItem( -1 ), m_tip( nullptr )
38{
39 m_list = new wxListCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
40 wxLC_REPORT | wxLC_SINGLE_SEL );
41 m_list->AppendColumn( _( "Title" ) );
42 m_list->AppendColumn( _( "Time" ) );
43 m_list->SetColumnWidth( 0, 200 );
44 m_list->SetColumnWidth( 1, 120 );
45
46 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
47 sizer->Add( m_list, 1, wxEXPAND );
48 SetSizer( sizer );
49
50 m_list->Bind( wxEVT_MOTION, &LOCAL_HISTORY_PANE::OnMotion, this );
51 m_list->Bind( wxEVT_LEAVE_WINDOW, &LOCAL_HISTORY_PANE::OnLeave, this );
52 m_list->Bind( wxEVT_LIST_ITEM_RIGHT_CLICK, &LOCAL_HISTORY_PANE::OnRightClick, this );
53 Bind( wxEVT_TIMER, &LOCAL_HISTORY_PANE::OnTimer, this, m_timer.GetId() );
54 Bind( wxEVT_TIMER, &LOCAL_HISTORY_PANE::OnRefreshTimer, this, m_refreshTimer.GetId() );
55 Bind( EVT_LOCAL_HISTORY_REFRESH, &LOCAL_HISTORY_PANE::OnRefreshEvent, this );
56
57 // Start refresh timer to check for updates every 10 seconds
58 m_refreshTimer.Start( 10000 );
59}
60
62{
63 m_timer.Stop();
64 m_refreshTimer.Stop();
65
66 if( m_tip )
67 {
68 m_tip->Destroy();
69 m_tip = nullptr;
70 }
71}
72
73void LOCAL_HISTORY_PANE::RefreshHistory( const wxString& aProjectPath )
74{
75 std::lock_guard<std::mutex> lock( m_mutex );
76
77 m_list->DeleteAllItems();
78 m_commits.clear();
79
80 wxFileName hist( aProjectPath, wxS( ".history" ) );
81
82 git_repository* repo = nullptr;
83 if( git_repository_open( &repo, hist.GetFullPath().mb_str().data() ) != 0 )
84 return;
85
86 git_revwalk* walk = nullptr;
87 if( git_revwalk_new( &walk, repo ) != 0 )
88 {
89 git_repository_free( repo );
90 return;
91 }
92
93 if( git_revwalk_push_head( walk ) != 0 )
94 {
95 git_revwalk_free( walk );
96 git_repository_free( repo );
97 return;
98 }
99
100 wxDateTime now = wxDateTime::Now();
101
102 git_oid oid;
103 while( git_revwalk_next( &oid, walk ) == 0 )
104 {
105 git_commit* commit = nullptr;
106 if( git_commit_lookup( &commit, repo, &oid ) != 0 )
107 continue;
108
110 info.hash = wxString::FromUTF8( git_oid_tostr_s( &oid ) );
111 info.summary = wxString::FromUTF8( git_commit_summary( commit ) );
112 info.message = wxString::FromUTF8( git_commit_message( commit ) );
113 info.date = wxDateTime( static_cast<time_t>( git_commit_time( commit ) ) );
114
115 // Calculate relative time
116 wxTimeSpan elapsed = now - info.date;
117 wxString timeStr;
118
119 if( elapsed.GetMinutes() < 1 )
120 {
121 timeStr = _( "Moments ago" );
122 }
123 else if( elapsed.GetMinutes() < 91 )
124 {
125 int minutes = elapsed.GetMinutes();
126 timeStr = wxString::Format( _( "%d minutes ago" ), minutes );
127 }
128 else if( elapsed.GetHours() < 24 )
129 {
130 int hours = elapsed.GetHours();
131 timeStr = wxString::Format( _( "%d hours ago" ), hours );
132 }
133 else
134 {
135 timeStr = info.date.FormatISOCombined();
136 }
137
138 long row = m_list->InsertItem( m_list->GetItemCount(), info.summary );
139 m_list->SetItem( row, 1, timeStr );
140
141 if( info.summary.StartsWith( wxS( "Autosave" ) ) )
142 m_list->SetItemBackgroundColour( row, wxColour( 230, 255, 230 ) );
143 else if( info.summary.StartsWith( wxS( "Backup" ) ) )
144 m_list->SetItemBackgroundColour( row, wxColour( 230, 230, 255 ) );
145
146 m_commits.emplace_back( std::move( info ) );
147 git_commit_free( commit );
148 }
149
150 git_revwalk_free( walk );
151 git_repository_free( repo );
152}
153
154void LOCAL_HISTORY_PANE::OnMotion( wxMouseEvent& aEvent )
155{
156 std::lock_guard<std::mutex> lock( m_mutex );
157
158 int flags = 0;
159 long item = m_list->HitTest( aEvent.GetPosition(), flags );
160
161 if( item != m_hoverItem )
162 {
163 if( m_hoverItem != -1 )
164 m_list->SetItemState( m_hoverItem, 0, wxLIST_STATE_SELECTED );
165
166 m_hoverItem = item;
167 m_hoverPos = aEvent.GetPosition();
168
169 if( m_hoverItem != -1 )
170 m_list->SetItemState( m_hoverItem, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
171
172 m_timer.StartOnce( 500 );
173 }
174
175 aEvent.Skip();
176}
177
178void LOCAL_HISTORY_PANE::OnLeave( wxMouseEvent& aEvent )
179{
180 std::lock_guard<std::mutex> lock( m_mutex );
181
182 m_timer.Stop();
183
184 if( m_hoverItem != -1 )
185 m_list->SetItemState( m_hoverItem, 0, wxLIST_STATE_SELECTED );
186
187 m_hoverItem = -1;
188
189 if( m_tip )
190 {
191 m_tip->Destroy();
192 m_tip = nullptr;
193 }
194
195 aEvent.Skip();
196}
197
198void LOCAL_HISTORY_PANE::OnTimer( wxTimerEvent& aEvent )
199{
200 std::lock_guard<std::mutex> lock( m_mutex );
201
202 if( m_hoverItem < 0 || m_hoverItem >= static_cast<long>( m_commits.size() ) )
203 return;
204
205 if( m_tip )
206 {
207 m_tip->Destroy();
208 m_tip = nullptr;
209 }
210
211 wxString msg = m_commits[m_hoverItem].message + wxS( "\n" )
212 + m_commits[m_hoverItem].date.FormatISOCombined();
213 m_tip = new wxTipWindow( this, msg );
214 m_tip->SetTipWindowPtr( &m_tip );
215 m_tip->Position( ClientToScreen( m_hoverPos ), wxDefaultSize );
216 SetFocus();
217}
218
219void LOCAL_HISTORY_PANE::OnRightClick( wxListEvent& aEvent )
220{
221 // Temporarily disable right-click context menu (todo: SNH)
222 return;
223
224 long item = aEvent.GetIndex();
225
226 if( item < 0 || item >= static_cast<long>( m_commits.size() ) )
227 return;
228
229 wxMenu menu;
230 wxMenuItem* restore = menu.Append( wxID_ANY, _( "Restore Commit" ) );
231 menu.Bind( wxEVT_MENU,
232 [this, item]( wxCommandEvent& )
233 {
234 m_frame->RestoreCommitFromHistory( m_commits[item].hash );
235 },
236 restore->GetId() );
237 PopupMenu( &menu );
238}
239
240
241void LOCAL_HISTORY_PANE::OnRefreshEvent( wxCommandEvent& aEvent )
242{
243 wxString projectPath = aEvent.GetString();
244
245 if( projectPath.IsEmpty() )
246 projectPath = m_frame->Prj().GetProjectPath();
247
248 RefreshHistory( projectPath );
249}
250
251
252void LOCAL_HISTORY_PANE::OnRefreshTimer( wxTimerEvent& aEvent )
253{
254 // Refresh the history to show updates from autosave
255 RefreshHistory( m_frame->Prj().GetProjectPath() );
256}
The main KiCad project manager frame.
LOCAL_HISTORY_PANE(KICAD_MANAGER_FRAME *aParent)
void OnLeave(wxMouseEvent &aEvent)
void OnRefreshTimer(wxTimerEvent &aEvent)
void OnTimer(wxTimerEvent &aEvent)
void OnRightClick(wxListEvent &aEvent)
KICAD_MANAGER_FRAME * m_frame
void RefreshHistory(const wxString &aProjectPath)
void OnRefreshEvent(wxCommandEvent &aEvent)
std::vector< LOCAL_COMMIT_INFO > m_commits
void OnMotion(wxMouseEvent &aEvent)
#define _(s)
wxDEFINE_EVENT(EVT_LOCAL_HISTORY_REFRESH, wxCommandEvent)