GitProgressDlg: Move most part of function to GitProgressList
[TortoiseGit.git] / src / TortoiseProc / GitProgressList.cpp
blob06ea26b05b2a13d149b71cd42d92de9556a995d9
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2013 - TortoiseGit
4 // Copyright (C) 2003-2008 - TortoiseSVN
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 2
9 // of the License, or (at your option) any later version.
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.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "GitProgressList.h"
22 #include "TortoiseProc.h"
23 #include "messagebox.h"
24 #include "GITProgressDlg.h"
25 #include "LogDlg.h"
26 #include "TGitPath.h"
27 #include "registry.h"
28 #include "GitStatus.h"
29 #include "AppUtils.h"
30 #include "PathUtils.h"
31 #include "StringUtils.h"
32 #include "TempFile.h"
33 #include "UnicodeUtils.h"
34 #include "SoundUtils.h"
35 #include "GitDiff.h"
36 #include "Hooks.h"
37 #include "DropFiles.h"
38 //#include "GitLogHelper.h"
39 #include "RegHistory.h"
40 //#include "ConflictResolveDlg.h"
41 #include "LogFile.h"
42 #include "ShellUpdater.h"
43 #include "IconMenu.h"
44 #include "BugTraqAssociations.h"
45 #include "patch.h"
46 #include "MassiveGitTask.h"
47 #include "SmartHandle.h"
49 static UINT WM_GITPROGRESS = RegisterWindowMessage(_T("TORTOISEGIT_GITPROGRESS_MSG"));
51 BOOL CGitProgressList::m_bAscending = FALSE;
52 int CGitProgressList::m_nSortedColumn = -1;
54 #define TRANSFERTIMER 100
55 #define VISIBLETIMER 101
56 // CGitProgressList
58 enum GITProgressDlgContextMenuCommands
60 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
61 ID_COMPARE = 1,
62 ID_EDITCONFLICT,
63 ID_CONFLICTRESOLVE,
64 ID_CONFLICTUSETHEIRS,
65 ID_CONFLICTUSEMINE,
66 ID_LOG,
67 ID_OPEN,
68 ID_OPENWITH,
69 ID_EXPLORE,
70 ID_COPY
73 IMPLEMENT_DYNAMIC(CGitProgressList, CListCtrl)
75 CGitProgressList::CGitProgressList():CListCtrl()
76 , m_bCancelled(FALSE)
77 , m_pThread(NULL)
78 , m_bErrorsOccurred(false)
79 , m_bBare(false)
80 , m_bNoCheckout(false)
81 , m_AutoTag(GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
82 , m_options(ProgOptNone)
87 CGitProgressList::~CGitProgressList()
89 for (size_t i = 0; i < m_arData.size(); ++i)
91 delete m_arData[i];
93 if(m_pThread != NULL)
95 delete m_pThread;
100 BEGIN_MESSAGE_MAP(CGitProgressList, CListCtrl)
101 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawSvnprogress)
102 ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkSvnprogress)
103 ON_NOTIFY_REFLECT(HDN_ITEMCLICK, OnHdnItemclickSvnprogress)
104 ON_REGISTERED_MESSAGE(WM_GITPROGRESS, OnGitProgress)
105 ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnLvnBegindragSvnprogress)
106 ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoSvnprogress)
107 ON_MESSAGE(WM_SHOWCONFLICTRESOLVER, OnShowConflictResolver)
108 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED, OnTaskbarBtnCreated)
109 ON_WM_SIZE()
110 ON_WM_TIMER()
111 ON_WM_CONTEXTMENU()
112 ON_WM_CREATE()
113 ON_WM_CLOSE()
114 END_MESSAGE_MAP()
116 BOOL CGitProgressList::Cancel()
118 return m_bCancelled;
123 // CGitProgressList message handlers
126 LRESULT CGitProgressList::OnShowConflictResolver(WPARAM /*wParam*/, LPARAM /*lParam*/)
128 #if 0
129 CConflictResolveDlg dlg(this);
130 const svn_wc_conflict_description_t *description = (svn_wc_conflict_description_t *)lParam;
131 if (description)
133 dlg.SetConflictDescription(description);
134 if (m_pTaskbarList)
136 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_PAUSED);
138 if (dlg.DoModal() == IDOK)
140 if (dlg.GetResult() == svn_wc_conflict_choose_postpone)
142 // if the result is conflicted and the dialog returned IDOK,
143 // that means we should not ask again in case of a conflict
144 m_AlwaysConflicted = true;
145 ::SendMessage(GetDlgItem(IDC_NONINTERACTIVE)->GetSafeHwnd(), BM_SETCHECK, BST_CHECKED, 0);
148 m_mergedfile = dlg.GetMergedFile();
149 m_bCancelled = dlg.IsCancelled();
150 if (m_pTaskbarList)
151 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_INDETERMINATE);
152 return dlg.GetResult();
155 return svn_wc_conflict_choose_postpone;
156 #endif
157 return 0;
159 #if 0
160 svn_wc_conflict_choice_t CGitProgressList::ConflictResolveCallback(const svn_wc_conflict_description_t *description, CString& mergedfile)
162 // we only bother the user when merging
163 if (((m_Command == GitProgress_Merge)||(m_Command == GitProgress_MergeAll)||(m_Command == GitProgress_MergeReintegrate))&&(!m_AlwaysConflicted)&&(description))
165 // we're in a worker thread here. That means we must not show a dialog from the thread
166 // but let the UI thread do it.
167 // To do that, we send a message to the UI thread and let it show the conflict resolver dialog.
168 LRESULT dlgResult = ::SendMessage(GetSafeHwnd(), WM_SHOWCONFLICTRESOLVER, 0, (LPARAM)description);
169 mergedfile = m_mergedfile;
170 return (svn_wc_conflict_choice_t)dlgResult;
173 return svn_wc_conflict_choose_postpone;
175 #endif
176 void CGitProgressList::AddItemToList()
178 int totalcount = GetItemCount();
180 SetItemCountEx(totalcount+1, LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
181 // make columns width fit
182 if (iFirstResized < 30)
184 // only resize the columns for the first 30 or so entries.
185 // after that, don't resize them anymore because that's an
186 // expensive function call and the columns will be sized
187 // close enough already.
188 ResizeColumns();
189 ++iFirstResized;
192 // Make sure the item is *entirely* visible even if the horizontal
193 // scroll bar is visible.
194 int count = GetCountPerPage();
195 if (totalcount <= (GetTopIndex() + count + nEnsureVisibleCount + 2))
197 ++nEnsureVisibleCount;
198 m_bLastVisible = true;
200 else
202 nEnsureVisibleCount = 0;
203 if (IsIconic() == 0)
204 m_bLastVisible = false;
209 BOOL CGitProgressList::Notify(const CTGitPath& path, git_wc_notify_action_t action,
210 int /*status*/ ,
211 CString *strErr
213 svn_node_kind_t kind, const CString& mime_type,
214 svn_wc_notify_state_t content_state,
215 svn_wc_notify_state_t prop_state, LONG rev,
216 const svn_lock_t * lock, svn_wc_notify_lock_state_t lock_state,
217 const CString& changelistname,
218 svn_merge_range_t * range,
219 svn_error_t * err, apr_pool_t * pool
222 bool bNoNotify = false;
223 bool bDoAddData = true;
224 NotificationData * data = new NotificationData();
225 data->path = path;
226 data->action = action;
227 data->sPathColumnText=path.GetGitPathString();
228 data->bAuxItem = false;
230 if (this->m_pAnimate)
231 this->m_pAnimate->ShowWindow(SW_HIDE);
233 #if 0
234 data->kind = kind;
235 data->mime_type = mime_type;
236 data->content_state = content_state;
237 data->prop_state = prop_state;
238 data->rev = rev;
239 data->lock_state = lock_state;
240 data->changelistname = changelistname;
241 if ((lock)&&(lock->owner))
242 data->owner = CUnicodeUtils::GetUnicode(lock->owner);
243 data->sPathColumnText = path.GetUIPathString();
244 if (!m_basePath.IsEmpty())
245 data->basepath = m_basePath;
246 if (range)
247 data->merge_range = *range;
248 #endif
249 switch (data->action)
251 case git_wc_notify_add:
252 //case svn_wc_notify_update_add:
253 // if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
254 // {
255 // data->color = m_Colors.GetColor(CColors::Conflict);
256 // data->bConflictedActionItem = true;
257 // data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
258 // ++m_nConflicts;
259 // }
260 // else
261 // {
262 // m_bMergesAddsDeletesOccurred = true;
263 data->sActionColumnText.LoadString(IDS_SVNACTION_ADD);
264 data->color = m_Colors.GetColor(CColors::Added);
265 // }
266 break;
267 case git_wc_notify_sendmail_start:
268 data->bAuxItem = true;
269 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_START);
270 data->color = m_Colors.GetColor(CColors::Modified);
271 break;
273 case git_wc_notify_sendmail_error:
274 data->bAuxItem = true;
275 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_ERROR);
276 if(strErr)
277 data->sPathColumnText = *strErr;
278 else
279 data->sPathColumnText.Empty();
280 data->color = m_Colors.GetColor(CColors::Modified);
281 break;
283 case git_wc_notify_sendmail_done:
285 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_DONE);
286 data->sPathColumnText.Empty();
287 data->color = m_Colors.GetColor(CColors::Modified);
288 break;
290 case git_wc_notify_sendmail_retry:
291 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_RETRY);
292 data->sPathColumnText.Empty();
293 data->color = m_Colors.GetColor(CColors::Modified);
294 break;
297 case git_wc_notify_resolved:
298 data->sActionColumnText.LoadString(IDS_SVNACTION_RESOLVE);
299 break;
301 case git_wc_notify_revert:
302 data->sActionColumnText.LoadString(IDS_SVNACTION_REVERT);
303 break;
305 case git_wc_notify_checkout:
306 data->sActionColumnText.LoadString(IDS_PROGRS_CMD_CHECKOUT);
307 data->color = m_Colors.GetColor(CColors::Added);
308 data->bAuxItem = false;
309 break;
311 #if 0
312 case svn_wc_notify_commit_added:
313 data->sActionColumnText.LoadString(IDS_SVNACTION_ADDING);
314 data->color = m_Colors.GetColor(CColors::Added);
315 break;
316 case svn_wc_notify_copy:
317 data->sActionColumnText.LoadString(IDS_SVNACTION_COPY);
318 break;
319 case svn_wc_notify_commit_modified:
320 data->sActionColumnText.LoadString(IDS_SVNACTION_MODIFIED);
321 data->color = m_Colors.GetColor(CColors::Modified);
322 break;
323 case svn_wc_notify_delete:
324 case svn_wc_notify_update_delete:
325 data->sActionColumnText.LoadString(IDS_SVNACTION_DELETE);
326 m_bMergesAddsDeletesOccurred = true;
327 data->color = m_Colors.GetColor(CColors::Deleted);
328 break;
329 case svn_wc_notify_commit_deleted:
330 data->sActionColumnText.LoadString(IDS_SVNACTION_DELETING);
331 data->color = m_Colors.GetColor(CColors::Deleted);
332 break;
333 case svn_wc_notify_restore:
334 data->sActionColumnText.LoadString(IDS_SVNACTION_RESTORE);
335 break;
337 case svn_wc_notify_update_replace:
338 case svn_wc_notify_commit_replaced:
339 data->sActionColumnText.LoadString(IDS_SVNACTION_REPLACED);
340 data->color = m_Colors.GetColor(CColors::Deleted);
341 break;
342 case svn_wc_notify_exists:
343 if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
345 data->color = m_Colors.GetColor(CColors::Conflict);
346 data->bConflictedActionItem = true;
347 ++m_nConflicts;
348 data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
350 else if ((data->content_state == svn_wc_notify_state_merged) || (data->prop_state == svn_wc_notify_state_merged))
352 data->color = m_Colors.GetColor(CColors::Merged);
353 m_bMergesAddsDeletesOccurred = true;
354 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGED);
356 else
357 data->sActionColumnText.LoadString(IDS_SVNACTION_EXISTS);
358 break;
359 case svn_wc_notify_update_update:
360 // if this is an inoperative dir change, don't show the notification.
361 // an inoperative dir change is when a directory gets updated without
362 // any real change in either text or properties.
363 if ((kind == svn_node_dir)
364 && ((prop_state == svn_wc_notify_state_inapplicable)
365 || (prop_state == svn_wc_notify_state_unknown)
366 || (prop_state == svn_wc_notify_state_unchanged)))
368 bNoNotify = true;
369 break;
371 if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
373 data->color = m_Colors.GetColor(CColors::Conflict);
374 data->bConflictedActionItem = true;
375 ++m_nConflicts;
376 data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
378 else if ((data->content_state == svn_wc_notify_state_merged) || (data->prop_state == svn_wc_notify_state_merged))
380 data->color = m_Colors.GetColor(CColors::Merged);
381 m_bMergesAddsDeletesOccurred = true;
382 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGED);
384 else if (((data->content_state != svn_wc_notify_state_unchanged)&&(data->content_state != svn_wc_notify_state_unknown)) ||
385 ((data->prop_state != svn_wc_notify_state_unchanged)&&(data->prop_state != svn_wc_notify_state_unknown)))
387 data->sActionColumnText.LoadString(IDS_SVNACTION_UPDATE);
389 else
391 bNoNotify = true;
392 break;
394 if (lock_state == svn_wc_notify_lock_state_unlocked)
396 CString temp(MAKEINTRESOURCE(IDS_SVNACTION_UNLOCKED));
397 data->sActionColumnText += _T(", ") + temp;
399 break;
401 case svn_wc_notify_update_external:
402 // For some reason we build a list of externals...
403 m_ExtStack.AddHead(path.GetUIPathString());
404 data->sActionColumnText.LoadString(IDS_SVNACTION_EXTERNAL);
405 data->bAuxItem = true;
406 break;
408 case svn_wc_notify_update_completed:
410 data->sActionColumnText.LoadString(IDS_SVNACTION_COMPLETED);
411 data->bAuxItem = true;
412 bool bEmpty = !!m_ExtStack.IsEmpty();
413 if (!bEmpty)
414 data->sPathColumnText.Format(IDS_PROGRS_PATHATREV, (LPCTSTR)m_ExtStack.RemoveHead(), rev);
415 else
416 data->sPathColumnText.Format(IDS_PROGRS_ATREV, rev);
418 if ((m_nConflicts>0)&&(bEmpty))
420 // We're going to add another aux item - let's shove this current onto the list first
421 // I don't really like this, but it will do for the moment.
422 m_arData.push_back(data);
423 AddItemToList();
425 data = new NotificationData();
426 data->bAuxItem = true;
427 data->sActionColumnText.LoadString(IDS_PROGRS_CONFLICTSOCCURED_WARNING);
428 data->sPathColumnText.LoadString(IDS_PROGRS_CONFLICTSOCCURED);
429 data->color = m_Colors.GetColor(CColors::Conflict);
430 CSoundUtils::PlayTSVNWarning();
431 // This item will now be added after the switch statement
433 if (!m_basePath.IsEmpty())
434 m_FinishedRevMap[m_basePath.GetSVNApiPath(pool)] = rev;
435 m_RevisionEnd = rev;
436 m_bFinishedItemAdded = true;
438 break;
439 case svn_wc_notify_commit_postfix_txdelta:
440 data->sActionColumnText.LoadString(IDS_SVNACTION_POSTFIX);
441 break;
442 case svn_wc_notify_failed_revert:
443 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDREVERT);
444 break;
445 case svn_wc_notify_status_completed:
446 case svn_wc_notify_status_external:
447 data->sActionColumnText.LoadString(IDS_SVNACTION_STATUS);
448 break;
449 case svn_wc_notify_skip:
450 if ((content_state == svn_wc_notify_state_missing)||(content_state == svn_wc_notify_state_obstructed)||(content_state == svn_wc_notify_state_conflicted))
452 data->sActionColumnText.LoadString(IDS_SVNACTION_SKIPMISSING);
454 // The color settings dialog describes the red color with
455 // "possible or real conflict / obstructed" which also applies to
456 // skipped targets during a merge. So we just use the same color.
457 data->color = m_Colors.GetColor(CColors::Conflict);
459 else
460 data->sActionColumnText.LoadString(IDS_SVNACTION_SKIP);
461 break;
462 case svn_wc_notify_locked:
463 if ((lock)&&(lock->owner))
464 data->sActionColumnText.Format(IDS_SVNACTION_LOCKEDBY, (LPCTSTR)CUnicodeUtils::GetUnicode(lock->owner));
465 break;
466 case svn_wc_notify_unlocked:
467 data->sActionColumnText.LoadString(IDS_SVNACTION_UNLOCKED);
468 break;
469 case svn_wc_notify_failed_lock:
470 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDLOCK);
471 m_arData.push_back(data);
472 AddItemToList();
473 ReportError(SVN::GetErrorString(err));
474 bDoAddData = false;
475 if (err->apr_err == SVN_ERR_FS_OUT_OF_DATE)
476 m_bLockWarning = true;
477 if (err->apr_err == SVN_ERR_FS_PATH_ALREADY_LOCKED)
478 m_bLockExists = true;
479 break;
480 case svn_wc_notify_failed_unlock:
481 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDUNLOCK);
482 m_arData.push_back(data);
483 AddItemToList();
484 ReportError(SVN::GetErrorString(err));
485 bDoAddData = false;
486 if (err->apr_err == SVN_ERR_FS_OUT_OF_DATE)
487 m_bLockWarning = true;
488 break;
489 case svn_wc_notify_changelist_set:
490 data->sActionColumnText.Format(IDS_SVNACTION_CHANGELISTSET, (LPCTSTR)data->changelistname);
491 break;
492 case svn_wc_notify_changelist_clear:
493 data->sActionColumnText.LoadString(IDS_SVNACTION_CHANGELISTCLEAR);
494 break;
495 case svn_wc_notify_changelist_moved:
496 data->sActionColumnText.Format(IDS_SVNACTION_CHANGELISTMOVED, (LPCTSTR)data->changelistname);
497 break;
498 case svn_wc_notify_foreign_merge_begin:
499 case svn_wc_notify_merge_begin:
500 if (range == NULL)
501 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGEBEGINNONE);
502 else if ((data->merge_range.start == data->merge_range.end) || (data->merge_range.start == data->merge_range.end - 1))
503 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINSINGLE, data->merge_range.end);
504 else if (data->merge_range.start - 1 == data->merge_range.end)
505 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINSINGLEREVERSE, data->merge_range.start);
506 else if (data->merge_range.start < data->merge_range.end)
507 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINMULTIPLE, data->merge_range.start + 1, data->merge_range.end);
508 else
509 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINMULTIPLEREVERSE, data->merge_range.start, data->merge_range.end + 1);
510 data->bAuxItem = true;
511 break;
512 #endif
513 default:
514 break;
515 } // switch (data->action)
517 if (bNoNotify)
518 delete data;
519 else
521 if (bDoAddData)
523 m_arData.push_back(data);
524 AddItemToList();
525 if ((!data->bAuxItem) && (m_itemCount > 0))
527 CProgressCtrl * progControl = (CProgressCtrl *)GetDlgItem(IDC_PROGRESSBAR);
528 progControl->ShowWindow(SW_SHOW);
529 progControl->SetPos(m_itemCount);
530 progControl->SetRange32(0, m_itemCountTotal);
531 if (m_pTaskbarList)
533 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
534 m_pTaskbarList->SetProgressValue(m_hWnd, m_itemCountTotal - m_itemCount, m_itemCountTotal);
535 m_pTaskbarList->SetProgressValue(m_hWnd, m_itemCount, m_itemCountTotal);
539 //if ((action == svn_wc_notify_commit_postfix_txdelta)&&(bSecondResized == FALSE))
541 // ResizeColumns();
542 // bSecondResized = TRUE;
546 return TRUE;
550 CString CGitProgressList::BuildInfoString()
552 CString infotext;
553 if(this->m_Command == GitProgress_Resolve)
554 infotext = _T("You need commit your change after resolve conflict");
555 #if 0
557 CString temp;
558 int added = 0;
559 int copied = 0;
560 int deleted = 0;
561 int restored = 0;
562 int reverted = 0;
563 int resolved = 0;
564 int conflicted = 0;
565 int updated = 0;
566 int merged = 0;
567 int modified = 0;
568 int skipped = 0;
569 int replaced = 0;
571 for (size_t i=0; i<m_arData.size(); ++i)
573 const NotificationData * dat = m_arData[i];
574 switch (dat->action)
576 case svn_wc_notify_add:
577 case svn_wc_notify_update_add:
578 case svn_wc_notify_commit_added:
579 if (dat->bConflictedActionItem)
580 ++conflicted;
581 else
582 ++added;
583 break;
584 case svn_wc_notify_copy:
585 ++copied;
586 break;
587 case svn_wc_notify_delete:
588 case svn_wc_notify_update_delete:
589 case svn_wc_notify_commit_deleted:
590 ++deleted;
591 break;
592 case svn_wc_notify_restore:
593 ++restored;
594 break;
595 case svn_wc_notify_revert:
596 ++reverted;
597 break;
598 case svn_wc_notify_resolved:
599 ++resolved;
600 break;
601 case svn_wc_notify_update_update:
602 if (dat->bConflictedActionItem)
603 ++conflicted;
604 else if ((dat->content_state == svn_wc_notify_state_merged) || (dat->prop_state == svn_wc_notify_state_merged))
605 ++merged;
606 else
607 ++updated;
608 break;
609 case svn_wc_notify_commit_modified:
610 ++modified;
611 break;
612 case svn_wc_notify_skip:
613 ++skipped;
614 break;
615 case svn_wc_notify_commit_replaced:
616 ++replaced;
617 break;
620 if (conflicted)
622 temp.LoadString(IDS_SVNACTION_CONFLICTED);
623 infotext += temp;
624 temp.Format(_T(":%d "), conflicted);
625 infotext += temp;
627 if (skipped)
629 temp.LoadString(IDS_SVNACTION_SKIP);
630 infotext += temp;
631 infotext.AppendFormat(_T(":%d "), skipped);
633 if (merged)
635 temp.LoadString(IDS_SVNACTION_MERGED);
636 infotext += temp;
637 infotext.AppendFormat(_T(":%d "), merged);
639 if (added)
641 temp.LoadString(IDS_SVNACTION_ADD);
642 infotext += temp;
643 infotext.AppendFormat(_T(":%d "), added);
645 if (deleted)
647 temp.LoadString(IDS_SVNACTION_DELETE);
648 infotext += temp;
649 infotext.AppendFormat(_T(":%d "), deleted);
651 if (modified)
653 temp.LoadString(IDS_SVNACTION_MODIFIED);
654 infotext += temp;
655 infotext.AppendFormat(_T(":%d "), modified);
657 if (copied)
659 temp.LoadString(IDS_SVNACTION_COPY);
660 infotext += temp;
661 infotext.AppendFormat(_T(":%d "), copied);
663 if (replaced)
665 temp.LoadString(IDS_SVNACTION_REPLACED);
666 infotext += temp;
667 infotext.AppendFormat(_T(":%d "), replaced);
669 if (updated)
671 temp.LoadString(IDS_SVNACTION_UPDATE);
672 infotext += temp;
673 infotext.AppendFormat(_T(":%d "), updated);
675 if (restored)
677 temp.LoadString(IDS_SVNACTION_RESTORE);
678 infotext += temp;
679 infotext.AppendFormat(_T(":%d "), restored);
681 if (reverted)
683 temp.LoadString(IDS_SVNACTION_REVERT);
684 infotext += temp;
685 infotext.AppendFormat(_T(":%d "), reverted);
687 if (resolved)
689 temp.LoadString(IDS_SVNACTION_RESOLVE);
690 infotext += temp;
691 infotext.AppendFormat(_T(":%d "), resolved);
693 #endif
694 return infotext;
697 void CGitProgressList::SetSelectedList(const CTGitPathList& selPaths)
699 m_selectedPaths = selPaths;
702 void CGitProgressList::ResizeColumns()
704 SetRedraw(FALSE);
706 TCHAR textbuf[MAX_PATH];
708 int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
709 for (int col = 0; col <= maxcol; ++col)
711 // find the longest width of all items
712 int count = GetItemCount();
713 HDITEM hdi = {0};
714 hdi.mask = HDI_TEXT;
715 hdi.pszText = textbuf;
716 hdi.cchTextMax = sizeof(textbuf);
717 ((CHeaderCtrl*)(GetDlgItem(0)))->GetItem(col, &hdi);
718 int cx = GetStringWidth(hdi.pszText)+20; // 20 pixels for col separator and margin
720 for (int index = 0; index<count; ++index)
722 // get the width of the string and add 12 pixels for the column separator and margins
723 int linewidth = cx;
724 switch (col)
726 case 0:
727 linewidth = GetStringWidth(m_arData[index]->sActionColumnText) + 12;
728 break;
729 case 1:
730 linewidth = GetStringWidth(m_arData[index]->sPathColumnText) + 12;
731 break;
733 if (cx < linewidth)
734 cx = linewidth;
736 SetColumnWidth(col, cx);
739 SetRedraw(TRUE);
742 bool CGitProgressList::SetBackgroundImage(UINT nID)
744 return CAppUtils::SetListCtrlBackgroundImage(GetSafeHwnd(), nID);
747 void CGitProgressList::ReportGitError()
749 ReportError(CGit::GetLibGit2LastErr());
752 void CGitProgressList::ReportError(const CString& sError)
754 CSoundUtils::PlayTGitError();
755 ReportString(sError, CString(MAKEINTRESOURCE(IDS_ERR_ERROR)), m_Colors.GetColor(CColors::Conflict));
756 m_bErrorsOccurred = true;
759 void CGitProgressList::ReportWarning(const CString& sWarning)
761 CSoundUtils::PlayTGitWarning();
762 ReportString(sWarning, CString(MAKEINTRESOURCE(IDS_WARN_WARNING)), m_Colors.GetColor(CColors::Conflict));
765 void CGitProgressList::ReportNotification(const CString& sNotification)
767 CSoundUtils::PlayTGitNotification();
768 ReportString(sNotification, CString(MAKEINTRESOURCE(IDS_WARN_NOTE)));
771 void CGitProgressList::ReportCmd(const CString& sCmd)
773 ReportString(sCmd, CString(MAKEINTRESOURCE(IDS_PROGRS_CMDINFO)), m_Colors.GetColor(CColors::Cmd));
776 void CGitProgressList::ReportString(CString sMessage, const CString& sMsgKind, COLORREF color)
778 // instead of showing a dialog box with the error message or notification,
779 // just insert the error text into the list control.
780 // that way the user isn't 'interrupted' by a dialog box popping up!
782 // the message may be split up into different lines
783 // so add a new entry for each line of the message
784 while (!sMessage.IsEmpty())
786 NotificationData * data = new NotificationData();
787 data->bAuxItem = true;
788 data->sActionColumnText = sMsgKind;
789 if (sMessage.Find('\n')>=0)
790 data->sPathColumnText = sMessage.Left(sMessage.Find('\n'));
791 else
792 data->sPathColumnText = sMessage;
793 data->sPathColumnText.Trim(_T("\n\r"));
794 data->color = color;
795 if (sMessage.Find('\n')>=0)
797 sMessage = sMessage.Mid(sMessage.Find('\n'));
798 sMessage.Trim(_T("\n\r"));
800 else
801 sMessage.Empty();
802 m_arData.push_back(data);
803 AddItemToList();
807 UINT CGitProgressList::ProgressThreadEntry(LPVOID pVoid)
809 return ((CGitProgressList*)pVoid)->ProgressThread();
812 UINT CGitProgressList::ProgressThread()
814 // The SetParams function should have loaded something for us
816 CString temp;
817 CString sWindowTitle;
818 bool localoperation = false;
819 bool bSuccess = false;
820 m_AlwaysConflicted = false;
822 #if 0 //need
823 DialogEnableWindow(IDOK, FALSE);
824 DialogEnableWindow(IDCANCEL, TRUE);
825 #endif
826 // SetAndClearProgressInfo(m_hWnd);
827 m_itemCount = m_itemCountTotal;
829 InterlockedExchange(&m_bThreadRunning, TRUE);
830 iFirstResized = 0;
831 bSecondResized = FALSE;
832 m_bFinishedItemAdded = false;
833 CTime startTime = CTime::GetCurrentTime();
835 if (m_pTaskbarList)
836 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_INDETERMINATE);
838 switch (m_Command)
840 case GitProgress_Add:
841 bSuccess = CmdAdd(sWindowTitle, localoperation);
842 break;
843 case GitProgress_Copy:
844 bSuccess = CmdCopy(sWindowTitle, localoperation);
845 break;
846 case GitProgress_Export:
847 bSuccess = CmdExport(sWindowTitle, localoperation);
848 break;
849 case GitProgress_Rename:
850 bSuccess = CmdRename(sWindowTitle, localoperation);
851 break;
852 case GitProgress_Resolve:
853 bSuccess = CmdResolve(sWindowTitle, localoperation);
854 break;
855 case GitProgress_Revert:
856 bSuccess = CmdRevert(sWindowTitle, localoperation);
857 break;
858 case GitProgress_Switch:
859 bSuccess = CmdSwitch(sWindowTitle, localoperation);
860 break;
861 case GitProgress_SendMail:
862 bSuccess = CmdSendMail(sWindowTitle, localoperation);
863 break;
864 case GitProgress_Clone:
865 bSuccess = CmdClone(sWindowTitle, localoperation);
866 break;
867 case GitProgress_Fetch:
868 bSuccess = CmdFetch(sWindowTitle, localoperation);
869 break;
871 if (!bSuccess)
872 temp.LoadString(IDS_PROGRS_TITLEFAILED);
873 else
874 temp.LoadString(IDS_PROGRS_TITLEFIN);
875 sWindowTitle = sWindowTitle + _T(" ") + temp;
876 SetWindowText(sWindowTitle);
878 KillTimer(TRANSFERTIMER);
879 KillTimer(VISIBLETIMER);
881 if (m_pTaskbarList)
883 if (DidErrorsOccur())
885 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
886 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
888 else
889 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
892 #if 0//need
893 DialogEnableWindow(IDCANCEL, FALSE);
894 DialogEnableWindow(IDOK, TRUE);
895 #endif
897 CString info = BuildInfoString();
898 if (!bSuccess)
899 info.LoadString(IDS_PROGRS_INFOFAILED);
900 SetDlgItemText(IDC_INFOTEXT, info);
901 ResizeColumns();
902 CWnd * pWndOk = GetDlgItem(IDOK);
903 if (pWndOk && ::IsWindow(pWndOk->GetSafeHwnd()))
905 SendMessage(DM_SETDEFID, IDOK);
906 GetDlgItem(IDOK)->SetFocus();
909 CString sFinalInfo;
910 if (!m_sTotalBytesTransferred.IsEmpty())
912 CTimeSpan time = CTime::GetCurrentTime() - startTime;
913 temp.Format(IDS_PROGRS_TIME, (LONG)time.GetTotalMinutes(), (LONG)time.GetSeconds());
914 sFinalInfo.Format(IDS_PROGRS_FINALINFO, m_sTotalBytesTransferred, (LPCTSTR)temp);
915 SetDlgItemText(IDC_PROGRESSLABEL, sFinalInfo);
917 else
918 GetDlgItem(IDC_PROGRESSLABEL)->ShowWindow(SW_HIDE);
920 GetDlgItem(IDC_PROGRESSBAR)->ShowWindow(SW_HIDE);
922 if (!m_bFinishedItemAdded)
924 // there's no "finished: xxx" line at the end. We add one here to make
925 // sure the user sees that the command is actually finished.
926 NotificationData * data = new NotificationData();
927 data->bAuxItem = true;
928 data->sActionColumnText.LoadString(IDS_PROGRS_FINISHED);
929 m_arData.push_back(data);
930 AddItemToList();
933 int count = GetItemCount();
934 if ((count > 0)&&(m_bLastVisible))
935 EnsureVisible(count-1, FALSE);
937 CLogFile logfile;
938 if (logfile.Open())
940 logfile.AddTimeLine();
941 for (size_t i = 0; i < m_arData.size(); ++i)
943 NotificationData * data = m_arData[i];
944 temp.Format(_T("%-20s : %s"), (LPCTSTR)data->sActionColumnText, (LPCTSTR)data->sPathColumnText);
945 logfile.AddLine(temp);
947 if (!sFinalInfo.IsEmpty())
948 logfile.AddLine(sFinalInfo);
949 logfile.Close();
952 m_bCancelled = TRUE;
953 InterlockedExchange(&m_bThreadRunning, FALSE);
954 #if 0 //need
955 RefreshCursor();
956 #endif
958 #if 0
959 DWORD dwAutoClose = CRegStdDWORD(_T("Software\\TortoiseGit\\AutoClose"), CLOSE_MANUAL);
960 if (m_options & ProgOptDryRun)
961 dwAutoClose = 0; // dry run means progress dialog doesn't auto close at all
962 if (!m_bLastVisible)
963 dwAutoClose = 0;
964 if (m_dwCloseOnEnd != (DWORD)-1)
965 dwAutoClose = m_dwCloseOnEnd; // command line value has priority over setting value
966 if ((dwAutoClose == CLOSE_NOERRORS)&&(!m_bErrorsOccurred))
967 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
968 if ((dwAutoClose == CLOSE_NOCONFLICTS)&&(!m_bErrorsOccurred)&&(m_nConflicts==0))
969 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
970 if ((dwAutoClose == CLOSE_NOMERGES)&&(!m_bErrorsOccurred)&&(m_nConflicts==0)&&(!m_bMergesAddsDeletesOccurred))
971 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
972 if ((dwAutoClose == CLOSE_LOCAL)&&(!m_bErrorsOccurred)&&(m_nConflicts==0)&&(localoperation))
973 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
974 #endif
976 //Don't do anything here which might cause messages to be sent to the window
977 //The window thread is probably now blocked in OnOK if we've done an auto close
978 return 0;
981 void CGitProgressList::OnLvnGetdispinfoSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
983 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
985 if (pDispInfo)
987 if (pDispInfo->item.mask & LVIF_TEXT)
989 if (pDispInfo->item.iItem < (int)m_arData.size())
991 const NotificationData * data = m_arData[pDispInfo->item.iItem];
992 switch (pDispInfo->item.iSubItem)
994 case 0:
995 lstrcpyn(m_columnbuf, data->sActionColumnText, MAX_PATH);
996 break;
997 case 1:
998 lstrcpyn(m_columnbuf, data->sPathColumnText, pDispInfo->item.cchTextMax);
999 if (!data->bAuxItem)
1001 int cWidth = GetColumnWidth(1);
1002 cWidth = max(12, cWidth-12);
1003 CDC * pDC = GetDC();
1004 if (pDC != NULL)
1006 CFont * pFont = pDC->SelectObject(GetFont());
1007 PathCompactPath(pDC->GetSafeHdc(), m_columnbuf, cWidth);
1008 pDC->SelectObject(pFont);
1009 ReleaseDC(pDC);
1012 break;
1013 default:
1014 m_columnbuf[0] = 0;
1016 pDispInfo->item.pszText = m_columnbuf;
1020 *pResult = 0;
1023 void CGitProgressList::OnNMCustomdrawSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
1025 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
1027 // Take the default processing unless we set this to something else below.
1028 *pResult = CDRF_DODEFAULT;
1030 // First thing - check the draw stage. If it's the control's prepaint
1031 // stage, then tell Windows we want messages for every item.
1033 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
1035 *pResult = CDRF_NOTIFYITEMDRAW;
1037 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
1039 // This is the prepaint stage for an item. Here's where we set the
1040 // item's text color. Our return value will tell Windows to draw the
1041 // item itself, but it will use the new color we set here.
1043 // Tell Windows to paint the control itself.
1044 *pResult = CDRF_DODEFAULT;
1046 ASSERT(pLVCD->nmcd.dwItemSpec < m_arData.size());
1047 if(pLVCD->nmcd.dwItemSpec >= m_arData.size())
1049 return;
1051 const NotificationData * data = m_arData[pLVCD->nmcd.dwItemSpec];
1052 ASSERT(data != NULL);
1053 if (data == NULL)
1054 return;
1056 // Store the color back in the NMLVCUSTOMDRAW struct.
1057 pLVCD->clrText = data->color;
1061 void CGitProgressList::OnNMDblclkSvnprogress(NMHDR * /*pNMHDR*/, LRESULT * /*pResult*/)
1063 #if 0
1064 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1065 *pResult = 0;
1066 if (pNMLV->iItem < 0)
1067 return;
1068 if (m_options & ProgOptDryRun)
1069 return; //don't do anything in a dry-run.
1071 const NotificationData * data = m_arData[pNMLV->iItem];
1072 if (data == NULL)
1073 return;
1075 if (data->bConflictedActionItem)
1077 // We've double-clicked on a conflicted item - do a three-way merge on it
1078 SVNDiff::StartConflictEditor(data->path);
1080 else if ((data->action == svn_wc_notify_update_update) && ((data->content_state == svn_wc_notify_state_merged)||(GitProgress_Merge == m_Command)) || (data->action == svn_wc_notify_resolved))
1082 // This is a modified file which has been merged on update. Diff it against base
1083 CTGitPath temporaryFile;
1084 SVNDiff diff(this, this->m_hWnd, true);
1085 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1086 svn_revnum_t baseRev = 0;
1087 diff.DiffFileAgainstBase(data->path, baseRev);
1089 else if ((!data->bAuxItem)&&(data->path.Exists())&&(!data->path.IsDirectory()))
1091 bool bOpenWith = false;
1092 int ret = (int)ShellExecute(m_hWnd, NULL, data->path.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
1093 if (ret <= HINSTANCE_ERROR)
1094 bOpenWith = true;
1095 if (bOpenWith)
1097 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1098 cmd += data->path.GetWinPathString() + _T(" ");
1099 CAppUtils::LaunchApplication(cmd, NULL, false);
1102 #endif
1105 void CGitProgressList::OnHdnItemclickSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
1107 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
1108 if (m_bThreadRunning)
1109 return;
1110 if (m_nSortedColumn == phdr->iItem)
1111 m_bAscending = !m_bAscending;
1112 else
1113 m_bAscending = TRUE;
1114 m_nSortedColumn = phdr->iItem;
1115 Sort();
1117 CString temp;
1118 SetRedraw(FALSE);
1119 DeleteAllItems();
1120 SetItemCountEx (static_cast<int>(m_arData.size()));
1122 SetRedraw(TRUE);
1124 *pResult = 0;
1127 bool CGitProgressList::NotificationDataIsAux(const NotificationData* pData)
1129 return pData->bAuxItem;
1131 BOOL CGitProgressList::Notify(const git_wc_notify_action_t action, CString str, const git_oid *a, const git_oid *b)
1133 NotificationData * data = new NotificationData();
1134 data->action = action;
1135 data->bAuxItem = false;
1137 if (action == git_wc_notify_update_ref)
1139 data->m_NewHash = b->id;
1140 data->m_OldHash = a->id;
1141 data->sActionColumnText.LoadString(IDS_GITACTION_UPDATE_REF);
1142 data->sPathColumnText.Format(_T("%s\t %s -> %s"), str,
1143 data->m_OldHash.ToString().Left(g_Git.GetShortHASHLength()),
1144 data->m_NewHash.ToString().Left(g_Git.GetShortHASHLength()));
1148 m_arData.push_back(data);
1149 AddItemToList();
1151 if (m_pAnimate)
1152 m_pAnimate->Stop();
1153 if (m_pAnimate)
1154 m_pAnimate->ShowWindow(SW_HIDE);
1155 return TRUE;
1157 BOOL CGitProgressList::Notify(const git_wc_notify_action_t /*action*/, const git_transfer_progress *stat)
1159 static unsigned int start = 0;
1160 unsigned int dt = GetCurrentTime() - start;
1161 size_t ds;
1162 double speed = 0;
1164 if (m_bCancelled)
1165 return FALSE;
1167 if (dt > 100)
1169 start = GetCurrentTime();
1170 ds = stat->received_bytes - m_TotalBytesTransferred;
1171 speed = ds * 1000.0/dt;
1172 m_TotalBytesTransferred = stat->received_bytes;
1174 else
1176 return TRUE;
1179 CProgressCtrl * progControl = (CProgressCtrl *)GetDlgItem(IDC_PROGRESSBAR);
1181 int progress;
1182 progress = stat->received_objects + stat->indexed_objects;
1184 if ((stat->total_objects > 1000) && (!progControl->IsWindowVisible()))
1186 progControl->ShowWindow(SW_SHOW);
1187 if (m_pTaskbarList)
1188 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1190 if (!GetDlgItem(IDC_PROGRESSLABEL)->IsWindowVisible())
1191 GetDlgItem(IDC_PROGRESSLABEL)->ShowWindow(SW_SHOW);
1193 progControl->SetPos(progress);
1194 progControl->SetRange32(0, 2 * stat->total_objects);
1195 if (m_pTaskbarList)
1197 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1198 m_pTaskbarList->SetProgressValue(m_hWnd, progress, stat->total_objects);
1201 CString progText;
1202 if (stat->received_bytes < 1024)
1203 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALBYTESTRANSFERRED, (int64_t)stat->received_bytes);
1204 else if (stat->received_bytes < 1200000)
1205 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALTRANSFERRED, (int64_t)stat->received_bytes / 1024);
1206 else
1207 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALMBTRANSFERRED, (double)((double)stat->received_bytes / 1024000.0));
1209 CString str;
1210 if(speed < 1024)
1211 str.Format(_T("%fB/s"), speed);
1212 else if(speed < 1024 * 1024)
1213 str.Format(_T("%.2fKB/s"), speed / 1024);
1214 else
1215 str.Format(_T("%.2fMB/s"), speed / 1024000.0);
1217 progText.Format(IDS_SVN_PROGRESS_TOTALANDSPEED, (LPCTSTR)m_sTotalBytesTransferred, (LPCTSTR)str);
1218 SetDlgItemText(IDC_PROGRESSLABEL, progText);
1220 return TRUE;
1223 LRESULT CGitProgressList::OnGitProgress(WPARAM /*wParam*/, LPARAM /*lParam*/)
1225 #if 0
1226 SVNProgress * pProgressData = (SVNProgress *)lParam;
1227 CProgressCtrl * progControl = (CProgressCtrl *)GetDlgItem(IDC_PROGRESSBAR);
1228 if ((pProgressData->total > 1000)&&(!progControl->IsWindowVisible()))
1230 progControl->ShowWindow(SW_SHOW);
1231 if (m_pTaskbarList)
1232 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1234 if (((pProgressData->total < 0)&&(pProgressData->progress > 1000)&&(progControl->IsWindowVisible()))&&(m_itemCountTotal<0))
1236 progControl->ShowWindow(SW_HIDE);
1237 if (m_pTaskbarList)
1238 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_INDETERMINATE);
1240 if (!GetDlgItem(IDC_PROGRESSLABEL)->IsWindowVisible())
1241 GetDlgItem(IDC_PROGRESSLABEL)->ShowWindow(SW_SHOW);
1242 SetTimer(TRANSFERTIMER, 2000, NULL);
1243 if ((pProgressData->total > 0)&&(pProgressData->progress > 1000))
1245 progControl->SetPos((int)pProgressData->progress);
1246 progControl->SetRange32(0, (int)pProgressData->total);
1247 if (m_pTaskbarList)
1249 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1250 m_pTaskbarList->SetProgressValue(m_hWnd, pProgressData->progress, pProgressData->total);
1253 CString progText;
1254 if (pProgressData->overall_total < 1024)
1255 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALBYTESTRANSFERRED, pProgressData->overall_total);
1256 else if (pProgressData->overall_total < 1200000)
1257 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALTRANSFERRED, pProgressData->overall_total / 1024);
1258 else
1259 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALMBTRANSFERRED, (double)((double)pProgressData->overall_total / 1024000.0));
1260 progText.Format(IDS_SVN_PROGRESS_TOTALANDSPEED, (LPCTSTR)m_sTotalBytesTransferred, (LPCTSTR)pProgressData->SpeedString);
1261 SetDlgItemText(IDC_PROGRESSLABEL, progText);
1262 #endif
1263 return 0;
1266 void CGitProgressList::OnTimer(UINT_PTR nIDEvent)
1268 if (nIDEvent == TRANSFERTIMER)
1270 CString progText;
1271 CString progSpeed;
1272 progSpeed.Format(IDS_SVN_PROGRESS_BYTES_SEC, 0);
1273 progText.Format(IDS_SVN_PROGRESS_TOTALANDSPEED, (LPCTSTR)m_sTotalBytesTransferred, (LPCTSTR)progSpeed);
1274 SetDlgItemText(IDC_PROGRESSLABEL, progText);
1275 KillTimer(TRANSFERTIMER);
1277 if (nIDEvent == VISIBLETIMER)
1279 if (nEnsureVisibleCount)
1280 EnsureVisible(GetItemCount()-1, false);
1281 nEnsureVisibleCount = 0;
1285 void CGitProgressList::Sort()
1287 if(m_arData.size() < 2)
1289 return;
1292 // We need to sort the blocks which lie between the auxiliary entries
1293 // This is so that any aux data stays where it was
1294 NotificationDataVect::iterator actionBlockBegin;
1295 NotificationDataVect::iterator actionBlockEnd = m_arData.begin(); // We start searching from here
1297 for(;;)
1299 // Search to the start of the non-aux entry in the next block
1300 actionBlockBegin = std::find_if(actionBlockEnd, m_arData.end(), std::not1(std::ptr_fun(&CGitProgressList::NotificationDataIsAux)));
1301 if(actionBlockBegin == m_arData.end())
1303 // There are no more actions
1304 break;
1306 // Now search to find the end of the block
1307 actionBlockEnd = std::find_if(actionBlockBegin+1, m_arData.end(), std::ptr_fun(&CGitProgressList::NotificationDataIsAux));
1308 // Now sort the block
1309 std::sort(actionBlockBegin, actionBlockEnd, &CGitProgressList::SortCompare);
1313 bool CGitProgressList::SortCompare(const NotificationData * pData1, const NotificationData * pData2)
1315 int result = 0;
1316 switch (m_nSortedColumn)
1318 case 0: //action column
1319 result = pData1->sActionColumnText.Compare(pData2->sActionColumnText);
1320 break;
1321 case 1: //path column
1322 // Compare happens after switch()
1323 break;
1324 default:
1325 break;
1328 // Sort by path if everything else is equal
1329 if (result == 0)
1331 result = CTGitPath::Compare(pData1->path, pData2->path);
1334 if (!m_bAscending)
1335 result = -result;
1336 return result < 0;
1339 void CGitProgressList::OnContextMenu(CWnd* pWnd, CPoint point)
1341 if (m_options & ProgOptDryRun)
1342 return; // don't do anything in a dry-run.
1344 if (pWnd == this)
1346 int selIndex = GetSelectionMark();
1347 if ((point.x == -1) && (point.y == -1))
1349 // Menu was invoked from the keyboard rather than by right-clicking
1350 CRect rect;
1351 GetItemRect(selIndex, &rect, LVIR_LABEL);
1352 ClientToScreen(&rect);
1353 point = rect.CenterPoint();
1356 if ((selIndex >= 0)&&(!m_bThreadRunning))
1358 // entry is selected, thread has finished with updating so show the popup menu
1359 CIconMenu popup;
1360 if (popup.CreatePopupMenu())
1362 bool bAdded = false;
1363 NotificationData * data = m_arData[selIndex];
1364 if ((data)&&(!data->path.IsDirectory()))
1367 if (data->action == svn_wc_notify_update_update || data->action == svn_wc_notify_resolved)
1369 if (GetSelectedCount() == 1)
1371 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
1372 bAdded = true;
1376 if (data->bConflictedActionItem)
1378 if (GetSelectedCount() == 1)
1380 popup.AppendMenuIcon(ID_EDITCONFLICT, IDS_MENUCONFLICT,IDI_CONFLICT);
1381 popup.SetDefaultItem(ID_EDITCONFLICT, FALSE);
1382 popup.AppendMenuIcon(ID_CONFLICTRESOLVE, IDS_SVNPROGRESS_MENUMARKASRESOLVED,IDI_RESOLVE);
1384 popup.AppendMenuIcon(ID_CONFLICTUSETHEIRS, IDS_SVNPROGRESS_MENUUSETHEIRS,IDI_RESOLVE);
1385 popup.AppendMenuIcon(ID_CONFLICTUSEMINE, IDS_SVNPROGRESS_MENUUSEMINE,IDI_RESOLVE);
1387 else if ((data->content_state == svn_wc_notify_state_merged)||(GitProgress_Merge == m_Command)||(data->action == svn_wc_notify_resolved))
1388 popup.SetDefaultItem(ID_COMPARE, FALSE);
1390 if (GetSelectedCount() == 1)
1392 if ((data->action == git_wc_notify_add)||
1393 (data->action == git_wc_notify_revert)||
1394 (data->action == git_wc_notify_resolved)||
1395 (data->action == git_wc_notify_checkout)||
1396 (data->action == git_wc_notify_update_ref))
1398 popup.AppendMenuIcon(ID_LOG, IDS_MENULOG,IDI_LOG);
1399 popup.AppendMenu(MF_SEPARATOR, NULL);
1400 popup.AppendMenuIcon(ID_OPEN, IDS_LOG_POPUP_OPEN, IDI_OPEN);
1401 popup.AppendMenuIcon(ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
1402 bAdded = true;
1405 } // if ((data)&&(!data->path.IsDirectory()))
1406 if (GetSelectedCount() == 1)
1408 if (data)
1410 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1411 CTGitPath path = CTGitPath(sPath);
1412 if (!sPath.IsEmpty())
1414 if (path.GetDirectory().Exists())
1416 popup.AppendMenuIcon(ID_EXPLORE, IDS_SVNPROGRESS_MENUOPENPARENT, IDI_EXPLORER);
1417 bAdded = true;
1422 if (GetSelectedCount() > 0)
1424 if (bAdded)
1425 popup.AppendMenu(MF_SEPARATOR, NULL);
1426 popup.AppendMenuIcon(ID_COPY, IDS_LOG_POPUP_COPYTOCLIPBOARD,IDI_COPYCLIP);
1427 bAdded = true;
1429 if (bAdded)
1431 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1432 #if 0//need
1433 DialogEnableWindow(IDOK, FALSE);
1434 #endif
1435 //this->SetPromptApp(&theApp);
1436 theApp.DoWaitCursor(1);
1437 bool bOpenWith = false;
1438 switch (cmd)
1440 case ID_COPY:
1442 CString sLines;
1443 POSITION pos = GetFirstSelectedItemPosition();
1444 while (pos)
1446 int nItem = GetNextSelectedItem(pos);
1447 NotificationData * data = m_arData[nItem];
1448 if (data)
1450 sLines += data->sPathColumnText;
1451 sLines += _T("\r\n");
1454 sLines.TrimRight();
1455 if (!sLines.IsEmpty())
1457 CStringUtils::WriteAsciiStringToClipboard(sLines, GetSafeHwnd());
1460 break;
1461 case ID_EXPLORE:
1463 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1465 CTGitPath path = CTGitPath(sPath);
1466 ShellExecute(m_hWnd, _T("explore"), path.GetDirectory().GetWinPath(), NULL, path.GetDirectory().GetWinPath(), SW_SHOW);
1468 break;
1469 #if 0
1470 case ID_COMPARE:
1472 svn_revnum_t rev = -1;
1473 StringRevMap::iterator it = m_UpdateStartRevMap.end();
1474 if (data->basepath.IsEmpty())
1475 it = m_UpdateStartRevMap.begin();
1476 else
1477 it = m_UpdateStartRevMap.find(data->basepath.GetSVNApiPath(pool));
1478 if (it != m_UpdateStartRevMap.end())
1479 rev = it->second;
1480 // if the file was merged during update, do a three way diff between OLD, MINE, THEIRS
1481 if (data->content_state == svn_wc_notify_state_merged)
1483 CTGitPath basefile = CTempFiles::Instance().GetTempFilePath(false, data->path, rev);
1484 CTGitPath newfile = CTempFiles::Instance().GetTempFilePath(false, data->path, SVNRev::REV_HEAD);
1485 SVN svn;
1486 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_WC), rev, basefile))
1488 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1489 DialogEnableWindow(IDOK, TRUE);
1490 break;
1492 // If necessary, convert the line-endings on the file before diffing
1493 if ((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\ConvertBase"), TRUE))
1495 CTGitPath temporaryFile = CTempFiles::Instance().GetTempFilePath(false, data->path, SVNRev::REV_BASE);
1496 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_BASE), SVNRev(SVNRev::REV_BASE), temporaryFile))
1498 temporaryFile.Reset();
1499 break;
1501 else
1503 newfile = temporaryFile;
1507 SetFileAttributes(newfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1508 SetFileAttributes(basefile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1509 CString revname, wcname, basename;
1510 revname.Format(_T("%s Revision %ld"), (LPCTSTR)data->path.GetUIFileOrDirectoryName(), rev);
1511 wcname.Format(IDS_DIFF_WCNAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1512 basename.Format(IDS_DIFF_BASENAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1513 CAppUtils::StartExtMerge(basefile, newfile, data->path, data->path, basename, revname, wcname, CString(), true);
1515 else
1517 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, data->path, rev);
1518 SVN svn;
1519 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_WC), rev, tempfile))
1521 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1522 DialogEnableWindow(IDOK, TRUE);
1523 break;
1525 else
1527 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1528 CString revname, wcname;
1529 revname.Format(_T("%s Revision %ld"), (LPCTSTR)data->path.GetUIFileOrDirectoryName(), rev);
1530 wcname.Format(IDS_DIFF_WCNAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1531 CAppUtils::StartExtDiff(
1532 tempfile, data->path, revname, wcname,
1533 CAppUtils::DiffFlags().AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000)));
1537 break;
1538 case ID_EDITCONFLICT:
1540 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1541 SVNDiff::StartConflictEditor(CTGitPath(sPath));
1543 break;
1544 case ID_CONFLICTUSETHEIRS:
1545 case ID_CONFLICTUSEMINE:
1546 case ID_CONFLICTRESOLVE:
1548 svn_wc_conflict_choice_t result = svn_wc_conflict_choose_merged;
1549 switch (cmd)
1551 case ID_CONFLICTUSETHEIRS:
1552 result = svn_wc_conflict_choose_theirs_full;
1553 break;
1554 case ID_CONFLICTUSEMINE:
1555 result = svn_wc_conflict_choose_mine_full;
1556 break;
1557 case ID_CONFLICTRESOLVE:
1558 result = svn_wc_conflict_choose_merged;
1559 break;
1561 SVN svn;
1562 POSITION pos = GetFirstSelectedItemPosition();
1563 CString sResolvedPaths;
1564 while (pos)
1566 int nItem = GetNextSelectedItem(pos);
1567 NotificationData * data = m_arData[nItem];
1568 if (data)
1570 if (data->bConflictedActionItem)
1572 if (!svn.Resolve(data->path, result, FALSE))
1574 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1575 DialogEnableWindow(IDOK, TRUE);
1576 break;
1578 else
1580 data->color = ::GetSysColor(COLOR_WINDOWTEXT);
1581 data->action = svn_wc_notify_resolved;
1582 data->sActionColumnText.LoadString(IDS_SVNACTION_RESOLVE);
1583 data->bConflictedActionItem = false;
1584 m_nConflicts--;
1586 if (m_nConflicts==0)
1588 // When the last conflict is resolved we remove
1589 // the warning which we assume is in the last line.
1590 int nIndex = GetItemCount()-1;
1591 VERIFY(DeleteItem(nIndex));
1593 delete m_arData[nIndex];
1594 m_arData.pop_back();
1596 sResolvedPaths += data->path.GetWinPathString() + _T("\n");
1601 Invalidate();
1602 CString info = BuildInfoString();
1603 SetDlgItemText(IDC_INFOTEXT, info);
1605 if (!sResolvedPaths.IsEmpty())
1607 CString msg;
1608 msg.Format(IDS_SVNPROGRESS_RESOLVED, (LPCTSTR)sResolvedPaths);
1609 CMessageBox::Show(m_hWnd, msg, _T("TortoiseGit"), MB_OK | MB_ICONINFORMATION);
1612 break;
1613 #endif
1614 case ID_LOG:
1616 CString cmd = _T("/command:log");
1617 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1618 if(data->action == git_wc_notify_update_ref)
1620 cmd += _T(" /path:\"") + GetPathFromColumnText(CString()) + _T("\"");
1621 if (!data->m_OldHash.IsEmpty())
1622 cmd += _T(" /startrev:") + data->m_OldHash.ToString();
1623 if (!data->m_NewHash.IsEmpty())
1624 cmd += _T(" /endrev:") + data->m_NewHash.ToString();
1626 else
1627 cmd += _T(" /path:\"") + sPath + _T("\"");
1628 CAppUtils::RunTortoiseGitProc(cmd);
1630 break;
1631 case ID_OPENWITH:
1632 bOpenWith = true;
1633 case ID_OPEN:
1635 int ret = 0;
1636 CString sWinPath = GetPathFromColumnText(data->sPathColumnText);
1637 if (!bOpenWith)
1638 ret = (int)ShellExecute(this->m_hWnd, NULL, (LPCTSTR)sWinPath, NULL, NULL, SW_SHOWNORMAL);
1639 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
1641 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1642 cmd += sWinPath + _T(" ");
1643 CAppUtils::LaunchApplication(cmd, NULL, false);
1647 #if 0 //need
1648 DialogEnableWindow(IDOK, TRUE);
1649 #endif
1650 theApp.DoWaitCursor(-1);
1651 } // if (bAdded)
1657 void CGitProgressList::OnLvnBegindragSvnprogress(NMHDR* , LRESULT *pResult)
1659 //LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1660 #if 0
1661 int selIndex = GetSelectionMark();
1662 if (selIndex < 0)
1663 return;
1665 CDropFiles dropFiles; // class for creating DROPFILES struct
1667 int index;
1668 POSITION pos = GetFirstSelectedItemPosition();
1669 while ( (index = GetNextSelectedItem(pos)) >= 0 )
1671 NotificationData * data = m_arData[index];
1673 if ( data->kind==svn_node_file || data->kind==svn_node_dir )
1675 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1677 dropFiles.AddFile( sPath );
1681 if ( dropFiles.GetCount()>0 )
1683 dropFiles.CreateStructure();
1685 #endif
1686 *pResult = 0;
1689 void CGitProgressList::OnSize(UINT nType, int cx, int cy)
1691 CListCtrl::OnSize(nType, cx, cy);
1692 if ((nType == SIZE_RESTORED)&&(m_bLastVisible))
1694 if(!m_hWnd)
1695 return;
1697 int count = GetItemCount();
1698 if (count > 0)
1699 EnsureVisible(count-1, false);
1703 //////////////////////////////////////////////////////////////////////////
1704 /// commands
1705 //////////////////////////////////////////////////////////////////////////
1706 bool CGitProgressList::CmdAdd(CString& sWindowTitle, bool& localoperation)
1708 localoperation = true;
1709 sWindowTitle.LoadString(IDS_PROGRS_TITLE_ADD);
1710 CAppUtils::SetWindowTitle(m_hWnd, m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
1711 SetBackgroundImage(IDI_ADD_BKG);
1712 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_ADD)));
1714 if (CRegDWORD(_T("Software\\TortoiseGit\\UseLibgit2"), TRUE) == TRUE)
1716 git_repository *repo = NULL;
1717 git_index *index;
1719 CStringA gitdir = CUnicodeUtils::GetMulti(CTGitPath(g_Git.m_CurrentDir).GetGitPathString(), CP_UTF8);
1720 if (git_repository_open(&repo, gitdir.GetBuffer()))
1722 gitdir.ReleaseBuffer();
1723 ReportGitError();
1724 return false;
1726 gitdir.ReleaseBuffer();
1728 git_config * config;
1729 git_config_new(&config);
1731 CStringA projectConfigA = CUnicodeUtils::GetMulti(g_Git.GetGitLocalConfig(), CP_UTF8);
1732 if (git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 4, FALSE))
1734 projectConfigA.ReleaseBuffer();
1735 ReportGitError();
1736 git_config_free(config);
1737 git_repository_free(repo);
1738 return false;
1740 projectConfigA.ReleaseBuffer();
1741 CString globalConfig = g_Git.GetGitGlobalConfig();
1742 if (PathFileExists(globalConfig))
1744 CStringA globalConfigA = CUnicodeUtils::GetMulti(globalConfig, CP_UTF8);
1745 if (git_config_add_file_ondisk(config, globalConfigA.GetBuffer(), 3, FALSE))
1747 globalConfigA.ReleaseBuffer();
1748 ReportGitError();
1749 git_config_free(config);
1750 git_repository_free(repo);
1751 return false;
1753 globalConfigA.ReleaseBuffer();
1755 CString globalXDGConfig = g_Git.GetGitGlobalXDGConfig();
1756 if (PathFileExists(globalXDGConfig))
1758 CStringA globalXDGConfigA = CUnicodeUtils::GetMulti(globalXDGConfig, CP_UTF8);
1759 if (git_config_add_file_ondisk(config, globalXDGConfigA.GetBuffer(), 2, FALSE))
1761 globalXDGConfigA.ReleaseBuffer();
1762 ReportGitError();
1763 git_config_free(config);
1764 git_repository_free(repo);
1765 return false;
1767 globalXDGConfigA.ReleaseBuffer();
1769 CString systemConfig = g_Git.GetGitSystemConfig();
1770 if (!systemConfig.IsEmpty())
1772 CStringA systemConfigA = CUnicodeUtils::GetMulti(systemConfig, CP_UTF8);
1773 if (git_config_add_file_ondisk(config, systemConfigA.GetBuffer(), 1, FALSE))
1775 systemConfigA.ReleaseBuffer();
1776 ReportGitError();
1777 git_config_free(config);
1778 git_repository_free(repo);
1779 return false;
1781 systemConfigA.ReleaseBuffer();
1784 git_repository_set_config(repo, config);
1786 if (git_repository_index(&index, repo))
1788 ReportGitError();
1789 git_repository_free(repo);
1790 return false;
1792 if (git_index_read(index))
1794 ReportGitError();
1795 git_index_free(index);
1796 git_repository_free(repo);
1797 return false;
1800 for (int i = 0; i < m_targetPathList.GetCount(); ++i)
1802 if (git_index_add_bypath(index, CStringA(CUnicodeUtils::GetMulti(m_targetPathList[i].GetGitPathString(), CP_UTF8)).GetBuffer()))
1804 ReportGitError();
1805 git_index_free(index);
1806 git_repository_free(repo);
1807 return false;
1809 Notify(m_targetPathList[i],git_wc_notify_add);
1812 if (git_index_write(index))
1814 ReportGitError();
1815 git_index_free(index);
1816 git_repository_free(repo);
1817 return false;
1820 git_index_free(index);
1821 git_repository_free(repo);
1823 else
1825 CMassiveGitTask mgt(L"add -f");
1826 mgt.ExecuteWithNotify(&m_targetPathList, m_bCancelled, git_wc_notify_add, this, &CGitProgressList::Notify);
1829 CShellUpdater::Instance().AddPathsForUpdate(m_targetPathList);
1831 this->GetDlgItem(IDC_LOGBUTTON)->SetWindowText(_T("Commit ..."));
1832 this->GetDlgItem(IDC_LOGBUTTON)->ShowWindow(SW_SHOW);
1833 return true;
1836 bool CGitProgressList::CmdCopy(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1838 #if 0
1839 ASSERT(m_targetPathList.GetCount() == 1);
1840 sWindowTitle.LoadString(IDS_PROGRS_TITLE_COPY);
1841 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1842 SetBackgroundImage(IDI_COPY_BKG);
1844 CString sCmdInfo;
1845 sCmdInfo.Format(IDS_PROGRS_CMD_COPY,
1846 m_targetPathList[0].IsUrl() ? (LPCTSTR)m_targetPathList[0].GetSVNPathString() : m_targetPathList[0].GetWinPath(),
1847 (LPCTSTR)m_url.GetSVNPathString(), (LPCTSTR)m_Revision.ToString());
1848 ReportCmd(sCmdInfo);
1850 if (!Copy(m_targetPathList, m_url, m_Revision, m_pegRev, m_sMessage))
1852 ReportSVNError();
1853 return false;
1855 if (m_options & ProgOptSwitchAfterCopy)
1857 sCmdInfo.Format(IDS_PROGRS_CMD_SWITCH,
1858 m_targetPathList[0].GetWinPath(),
1859 (LPCTSTR)m_url.GetSVNPathString(), (LPCTSTR)m_Revision.ToString());
1860 ReportCmd(sCmdInfo);
1861 if (!Switch(m_targetPathList[0], m_url, SVNRev::REV_HEAD, SVNRev::REV_HEAD, m_depth, TRUE, m_options & ProgOptIgnoreExternals))
1863 if (!Switch(m_targetPathList[0], m_url, SVNRev::REV_HEAD, m_Revision, m_depth, TRUE, m_options & ProgOptIgnoreExternals))
1865 ReportSVNError();
1866 return false;
1870 else
1872 if (SVN::PathIsURL(m_url))
1874 CString sMsg(MAKEINTRESOURCE(IDS_PROGRS_COPY_WARNING));
1875 ReportNotification(sMsg);
1878 #endif
1879 return true;
1882 bool CGitProgressList::CmdExport(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1884 #if 0
1885 ASSERT(m_targetPathList.GetCount() == 1);
1886 sWindowTitle.LoadString(IDS_PROGRS_TITLE_EXPORT);
1887 sWindowTitle = m_url.GetUIFileOrDirectoryName()+_T(" - ")+sWindowTitle;
1888 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1889 SetBackgroundImage(IDI_EXPORT_BKG);
1890 CString eol;
1891 if (m_options & ProgOptEolCRLF)
1892 eol = _T("CRLF");
1893 if (m_options & ProgOptEolLF)
1894 eol = _T("LF");
1895 if (m_options & ProgOptEolCR)
1896 eol = _T("CR");
1897 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_EXPORT)));
1898 if (!Export(m_url, m_targetPathList[0], m_Revision, m_Revision, TRUE, m_options & ProgOptIgnoreExternals, m_depth, NULL, FALSE, eol))
1900 ReportSVNError();
1901 return false;
1903 #endif
1904 return true;
1907 bool CGitProgressList::CmdRename(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1909 #if 0
1910 ASSERT(m_targetPathList.GetCount() == 1);
1911 if ((!m_targetPathList[0].IsUrl())&&(!m_url.IsUrl()))
1912 localoperation = true;
1913 sWindowTitle.LoadString(IDS_PROGRS_TITLE_RENAME);
1914 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1915 SetBackgroundImage(IDI_RENAME_BKG);
1916 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RENAME)));
1917 if (!Move(m_targetPathList, m_url, m_Revision, m_sMessage))
1919 ReportSVNError();
1920 return false;
1922 #endif
1923 return true;
1926 bool CGitProgressList::CmdResolve(CString& sWindowTitle, bool& localoperation)
1929 localoperation = true;
1930 ASSERT(m_targetPathList.GetCount() == 1);
1931 sWindowTitle.LoadString(IDS_PROGRS_TITLE_RESOLVE);
1932 CAppUtils::SetWindowTitle(m_hWnd, m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
1933 SetBackgroundImage(IDI_RESOLVE_BKG);
1934 // check if the file may still have conflict markers in it.
1935 //BOOL bMarkers = FALSE;
1937 for (int i = 0; i < m_targetPathList.GetCount(); ++i)
1939 CString cmd,out,tempmergefile;
1940 cmd.Format(_T("git.exe add -f -- \"%s\""),m_targetPathList[i].GetGitPathString());
1941 if (g_Git.Run(cmd, &out, CP_UTF8))
1943 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1944 m_bErrorsOccurred=true;
1945 return false;
1948 CAppUtils::RemoveTempMergeFile((CTGitPath &)m_targetPathList[i]);
1950 Notify(m_targetPathList[i],git_wc_notify_resolved);
1952 #if 0
1953 if ((m_options & ProgOptSkipConflictCheck) == 0)
1957 for (INT_PTR fileindex=0; (fileindex<m_targetPathList.GetCount()) && (bMarkers==FALSE); ++fileindex)
1959 if (!m_targetPathList[fileindex].IsDirectory())
1961 CStdioFile file(m_targetPathList[fileindex].GetWinPath(), CFile::typeBinary | CFile::modeRead);
1962 CString strLine = _T("");
1963 while (file.ReadString(strLine))
1965 if (strLine.Find(_T("<<<<<<<"))==0)
1967 bMarkers = TRUE;
1968 break;
1971 file.Close();
1975 catch (CFileException* pE)
1977 TRACE(_T("CFileException in Resolve!\n"));
1978 TCHAR error[10000] = {0};
1979 pE->GetErrorMessage(error, 10000);
1980 ReportError(error);
1981 pE->Delete();
1982 return false;
1985 if (bMarkers)
1987 if (CMessageBox::Show(m_hWnd, IDS_PROGRS_REVERTMARKERS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION)==IDYES)
1989 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RESOLVE)));
1990 for (INT_PTR fileindex=0; fileindex<m_targetPathList.GetCount(); ++fileindex)
1991 Resolve(m_targetPathList[fileindex], svn_wc_conflict_choose_merged, true);
1994 else
1996 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RESOLVE)));
1997 for (INT_PTR fileindex=0; fileindex<m_targetPathList.GetCount(); ++fileindex)
1998 Resolve(m_targetPathList[fileindex], svn_wc_conflict_choose_merged, true);
2000 #endif
2001 CShellUpdater::Instance().AddPathsForUpdate(m_targetPathList);
2003 this->GetDlgItem(IDC_LOGBUTTON)->SetWindowText(CString(MAKEINTRESOURCE(IDS_COMMITBUTTON)));
2004 this->GetDlgItem(IDC_LOGBUTTON)->ShowWindow(SW_SHOW);
2006 return true;
2009 bool CGitProgressList::CmdRevert(CString& sWindowTitle, bool& localoperation)
2012 localoperation = true;
2013 sWindowTitle.LoadString(IDS_PROGRS_TITLE_REVERT);
2014 CAppUtils::SetWindowTitle(m_hWnd, m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
2015 SetBackgroundImage(IDI_REVERT_BKG);
2017 CTGitPathList delList;
2018 for (int i = 0; i < m_selectedPaths.GetCount(); ++i)
2020 CTGitPath path;
2021 int action;
2022 path.SetFromWin(g_Git.m_CurrentDir+_T("\\")+m_selectedPaths[i].GetWinPath());
2023 action = m_selectedPaths[i].m_Action;
2024 /* rename file can't delete because it needs original file*/
2025 if((!(action & CTGitPath::LOGACTIONS_ADDED)) &&
2026 (!(action & CTGitPath::LOGACTIONS_REPLACED)))
2027 delList.AddPath(path);
2029 if (DWORD(CRegDWORD(_T("Software\\TortoiseGit\\RevertWithRecycleBin"), TRUE)))
2030 delList.DeleteAllFiles(true);
2032 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_REVERT)));
2033 for (int i = 0; i < m_selectedPaths.GetCount(); ++i)
2035 if(g_Git.Revert(_T("HEAD"), (CTGitPath&)m_selectedPaths[i]))
2037 CMessageBox::Show(NULL,_T("Revert Fail"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
2038 m_bErrorsOccurred=true;
2039 return false;
2041 Notify(m_selectedPaths[i],git_wc_notify_revert);
2044 CShellUpdater::Instance().AddPathsForUpdate(m_selectedPaths);
2046 return true;
2049 bool CGitProgressList::CmdSwitch(CString& /*sWindowTitle*/, bool& /*localoperation*/)
2051 #if 0
2052 ASSERT(m_targetPathList.GetCount() == 1);
2053 SVNStatus st;
2054 sWindowTitle.LoadString(IDS_PROGRS_TITLE_SWITCH);
2055 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
2056 SetBackgroundImage(IDI_SWITCH_BKG);
2057 LONG rev = 0;
2058 if (st.GetStatus(m_targetPathList[0]) != (-2))
2060 if (st.status->entry != NULL)
2062 rev = st.status->entry->revision;
2066 CString sCmdInfo;
2067 sCmdInfo.Format(IDS_PROGRS_CMD_SWITCH,
2068 m_targetPathList[0].GetWinPath(), (LPCTSTR)m_url.GetSVNPathString(),
2069 (LPCTSTR)m_Revision.ToString());
2070 ReportCmd(sCmdInfo);
2072 bool depthIsSticky = true;
2073 if (m_depth == svn_depth_unknown)
2074 depthIsSticky = false;
2075 if (!Switch(m_targetPathList[0], m_url, m_Revision, m_Revision, m_depth, depthIsSticky, m_options & ProgOptIgnoreExternals))
2077 ReportSVNError();
2078 return false;
2080 m_UpdateStartRevMap[m_targetPathList[0].GetSVNApiPath(pool)] = rev;
2081 if ((m_RevisionEnd >= 0)&&(rev >= 0)
2082 &&((LONG)m_RevisionEnd > (LONG)rev))
2084 GetDlgItem(IDC_LOGBUTTON)->ShowWindow(SW_SHOW);
2086 #endif
2087 return true;
2089 bool CGitProgressList::CmdClone(CString& sWindowTitle, bool& /*localoperation*/)
2091 if (!g_Git.UsingLibGit2(CGit::GIT_CMD_CLONE))
2093 // should never run to here
2094 ASSERT(FALSE);
2095 return false;
2097 this->m_TotalBytesTransferred = 0;
2099 sWindowTitle.LoadString(IDS_PROGRS_TITLE_CLONE);
2100 CAppUtils::SetWindowTitle(m_hWnd, m_url.GetGitPathString(), sWindowTitle);
2101 SetBackgroundImage(IDI_SWITCH_BKG);
2102 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROG_CLONE)));
2104 if (m_url.IsEmpty() || m_targetPathList.GetCount() == 0)
2105 return false;
2107 CStringA url = CUnicodeUtils::GetMulti(m_url.GetGitPathString(), CP_UTF8);
2108 CStringA path = CUnicodeUtils::GetMulti(m_targetPathList[0].GetWinPathString(),CP_UTF8);
2110 git_repository *cloned_repo = NULL;
2111 git_remote *origin = NULL;
2112 git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
2114 int error = 0;
2116 git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
2118 clone_opts.checkout_opts = checkout_opts;
2120 clone_opts.checkout_opts.checkout_strategy = m_bNoCheckout? GIT_CHECKOUT_NONE : GIT_CHECKOUT_SAFE_CREATE;
2121 clone_opts.checkout_opts.progress_cb = CheckoutCallback;
2122 clone_opts.checkout_opts.progress_payload = this;
2124 git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
2126 callbacks.update_tips = RemoteUpdatetipsCallback;
2127 callbacks.progress = RemoteProgressCallback;
2128 callbacks.completion = RemoteCompletionCallback;
2129 callbacks.payload = this;
2131 clone_opts.remote_callbacks = &callbacks;
2133 if (!m_RefSpec.IsEmpty())
2134 clone_opts.checkout_branch = CUnicodeUtils::GetMulti(m_RefSpec, CP_UTF8).GetBuffer();
2136 clone_opts.fetch_progress_cb = FetchCallback;
2137 clone_opts.fetch_progress_payload = this;
2139 clone_opts.cred_acquire_cb = CAppUtils::Git2GetUserPassword;
2141 clone_opts.bare = m_bBare;
2143 if(m_pAnimate)
2145 m_pAnimate->ShowWindow(SW_SHOW);
2146 m_pAnimate->Play(0, INT_MAX, INT_MAX);
2148 error = git_clone(&cloned_repo, url, path, &clone_opts);
2150 if (m_pAnimate)
2152 m_pAnimate->Stop();
2153 m_pAnimate->ShowWindow(SW_HIDE);
2156 git_remote_free(origin);
2157 if (error)
2159 ReportGitError();
2160 return false;
2162 else if (cloned_repo)
2163 git_repository_free(cloned_repo);
2164 return true;
2166 bool CGitProgressList::CmdSendMail(CString& sWindowTitle, bool& /*localoperation*/)
2168 sWindowTitle.LoadString(IDS_PROGRS_TITLE_SENDMAIL);
2169 CAppUtils::SetWindowTitle(m_hWnd, m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
2170 //SetBackgroundImage(IDI_ADD_BKG);
2171 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_SENDMAIL)));
2172 bool ret=true;
2173 if(this->m_SendMailFlags&SENDMAIL_COMBINED)
2175 CString error;
2176 CTGitPath path;
2177 Notify(path,git_wc_notify_sendmail_start);
2178 CString err;
2179 int retry=0;
2180 while(retry <3)
2182 if(!!CPatch::SendPatchesCombined(m_targetPathList,m_SendMailTO,m_SendMailCC,m_SendMailSubject,!!(this->m_SendMailFlags&SENDMAIL_ATTACHMENT),!!(this->m_SendMailFlags&SENDMAIL_MAPI),&err))
2184 Notify(path,git_wc_notify_sendmail_error,ret,&err);
2185 ret = false;
2187 else
2189 break;
2192 ++retry;
2193 if (retry < 3)
2194 Notify(path,git_wc_notify_sendmail_retry,ret,&err);
2195 Sleep(2000);
2196 if(m_bCancelled)
2198 CString str;
2199 str.LoadString(IDS_SVN_USERCANCELLED);
2200 Notify(path,git_wc_notify_sendmail_error,ret,&str);
2201 return false;
2204 if (ret)
2205 Notify(path,git_wc_notify_sendmail_done,ret);
2207 else
2209 for (int i = 0; ret && i < m_targetPathList.GetCount(); ++i)
2211 CPatch patch;
2212 Notify(m_targetPathList[i],git_wc_notify_sendmail_start);
2214 int retry=0;
2215 while(retry<3)
2217 if(!!patch.Send((CString&)m_targetPathList[i].GetWinPathString(),this->m_SendMailTO,
2218 this->m_SendMailCC,!!(this->m_SendMailFlags&SENDMAIL_ATTACHMENT),!!(this->m_SendMailFlags&SENDMAIL_MAPI)))
2220 Notify(m_targetPathList[i],git_wc_notify_sendmail_error,ret,&patch.m_LastError);
2221 ret = false;
2224 else
2226 ret = true;
2227 break;
2229 ++retry;
2230 if (retry < 3)
2231 Notify(m_targetPathList[i],git_wc_notify_sendmail_retry,ret,&patch.m_LastError);
2232 Sleep(2000);
2233 if(m_bCancelled)
2235 CString str;
2236 str.LoadString(IDS_SVN_USERCANCELLED);
2237 Notify(m_targetPathList[i],git_wc_notify_sendmail_error,ret,&str);
2238 return false;
2241 if (ret)
2242 Notify(m_targetPathList[i],git_wc_notify_sendmail_done,ret);
2245 return ret;
2248 LRESULT CGitProgressList::OnTaskbarBtnCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
2250 m_pTaskbarList.Release();
2251 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
2252 return 0;
2255 bool CGitProgressList::CmdFetch(CString& sWindowTitle, bool& /*localoperation*/)
2257 if (!g_Git.UsingLibGit2(CGit::GIT_CMD_CLONE))
2259 // should never run to here
2260 ASSERT(0);
2261 return false;
2263 this->m_TotalBytesTransferred = 0;
2265 sWindowTitle.LoadString(IDS_PROGRS_TITLE_FETCH);
2266 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
2267 SetBackgroundImage(IDI_UPDATE_BKG);
2268 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_TITLE_FETCH)));
2270 CStringA url = CUnicodeUtils::GetMulti(m_url.GetGitPathString(), CP_UTF8);
2271 CStringA gitdir = CUnicodeUtils::GetMulti(CTGitPath(g_Git.m_CurrentDir).GetGitPathString(), CP_UTF8);
2272 CStringA remotebranch = CUnicodeUtils::GetMulti(m_RefSpec, CP_UTF8);
2274 git_remote *remote = NULL;
2275 git_repository *repo = NULL;
2276 bool ret = true;
2280 if (git_repository_open(&repo, gitdir.GetBuffer()))
2282 gitdir.ReleaseBuffer();
2283 ReportGitError();
2284 ret = false;
2285 break;
2288 // first try with a named remote (e.g. "origin")
2289 if (git_remote_load(&remote, repo, url) < 0)
2291 // retry with repository located at a specific url
2292 if (git_remote_create_inmemory(&remote, repo, NULL, url) < 0)
2294 ReportGitError();
2295 ret = false;
2296 break;
2300 git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
2302 callbacks.update_tips = RemoteUpdatetipsCallback;
2303 callbacks.progress = RemoteProgressCallback;
2304 callbacks.completion = RemoteCompletionCallback;
2305 callbacks.payload = this;
2307 git_remote_set_callbacks(remote, &callbacks);
2308 git_remote_set_cred_acquire_cb(remote, CAppUtils::Git2GetUserPassword, NULL);
2309 git_remote_set_autotag(remote, (git_remote_autotag_option_t)m_AutoTag);
2311 if (!remotebranch.IsEmpty() && git_remote_set_fetchspec(remote, remotebranch))
2313 ReportGitError();
2314 ret = false;
2315 break;
2318 if (m_pAnimate)
2320 m_pAnimate->ShowWindow(SW_SHOW);
2321 m_pAnimate->Play(0, INT_MAX, INT_MAX);
2323 // Connect to the remote end specifying that we want to fetch
2324 // information from it.
2325 if (git_remote_connect(remote, GIT_DIRECTION_FETCH) < 0) {
2326 ReportGitError();
2327 ret = false;
2328 break;
2331 // Download the packfile and index it. This function updates the
2332 // amount of received data and the indexer stats which lets you
2333 // inform the user about progress.
2334 if (git_remote_download(remote, FetchCallback, this) < 0) {
2335 ReportGitError();
2336 ret = false;
2337 break;
2340 if (m_pAnimate)
2342 m_pAnimate->ShowWindow(SW_HIDE);
2344 // Update the references in the remote's namespace to point to the
2345 // right commits. This may be needed even if there was no packfile
2346 // to download, which can happen e.g. when the branches have been
2347 // changed but all the neede objects are available locally.
2348 if (git_remote_update_tips(remote) < 0)
2350 ReportGitError();
2351 ret = false;
2352 break;
2355 git_remote_disconnect(remote);
2357 } while(0);
2359 git_remote_free(remote);
2360 git_repository_free(repo);
2361 if (m_pAnimate)
2362 m_pAnimate->ShowWindow(SW_HIDE);
2363 return ret;
2367 int CGitProgressList::OnCreate(LPCREATESTRUCT lpCreateStruct)
2369 if (CListCtrl::OnCreate(lpCreateStruct) == -1)
2370 return -1;
2372 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
2373 // do this, Explorer would be unable to send that message to our window if we
2374 // were running elevated. It's OK to make the call all the time, since if we're
2375 // not elevated, this is a no-op.
2376 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
2377 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
2378 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(_T("user32.dll"));
2379 if (hUser)
2381 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
2382 if (pfnChangeWindowMessageFilterEx)
2384 pfnChangeWindowMessageFilterEx(m_hWnd, WM_TASKBARBTNCREATED, MSGFLT_ALLOW, &cfs);
2387 m_pTaskbarList.Release();
2388 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
2389 m_pTaskbarList = nullptr;
2391 SetExtendedStyle (LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
2393 DeleteAllItems();
2394 int c = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
2395 while (c>=0)
2396 DeleteColumn(c--);
2398 CString temp;
2399 temp.LoadString(IDS_PROGRS_ACTION);
2400 InsertColumn(0, temp);
2401 temp.LoadString(IDS_PROGRS_PATH);
2402 InsertColumn(1, temp);
2404 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
2405 if (m_pThread==NULL)
2407 ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
2409 else
2411 m_pThread->m_bAutoDelete = FALSE;
2412 m_pThread->ResumeThread();
2415 // Call this early so that the column headings aren't hidden before any
2416 // text gets added.
2417 ResizeColumns();
2419 SetTimer(VISIBLETIMER, 300, NULL);
2421 return 0;
2425 void CGitProgressList::OnClose()
2427 // TODO: Add your message handler code here and/or call default
2428 if (m_bCancelled)
2430 TerminateThread(m_pThread->m_hThread, (DWORD)-1);
2431 InterlockedExchange(&m_bThreadRunning, FALSE);
2433 else
2435 m_bCancelled = TRUE;
2436 return;
2438 CListCtrl::OnClose();
2442 BOOL CGitProgressList::PreTranslateMessage(MSG* pMsg)
2444 // TODO: Add your specialized code here and/or call the base class
2445 if (pMsg->message == WM_KEYDOWN)
2447 if (pMsg->wParam == 'A')
2449 if (GetKeyState(VK_CONTROL)&0x8000)
2451 // Ctrl-A -> select all
2452 SetSelectionMark(0);
2453 for (int i=0; i<GetItemCount(); ++i)
2455 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2459 if ((pMsg->wParam == 'C')||(pMsg->wParam == VK_INSERT))
2461 int selIndex = GetSelectionMark();
2462 if (selIndex >= 0)
2464 if (GetKeyState(VK_CONTROL)&0x8000)
2466 //Ctrl-C -> copy to clipboard
2467 CString sClipdata;
2468 POSITION pos = GetFirstSelectedItemPosition();
2469 if (pos != NULL)
2471 while (pos)
2473 int nItem = GetNextSelectedItem(pos);
2474 CString sAction = GetItemText(nItem, 0);
2475 CString sPath = GetItemText(nItem, 1);
2476 CString sMime = GetItemText(nItem, 2);
2477 CString sLogCopyText;
2478 sLogCopyText.Format(_T("%s: %s %s\r\n"),
2479 (LPCTSTR)sAction, (LPCTSTR)sPath, (LPCTSTR)sMime);
2480 sClipdata += sLogCopyText;
2482 CStringUtils::WriteAsciiStringToClipboard(sClipdata);
2487 } // if (pMsg->message == WM_KEYDOWN)
2488 return CListCtrl::PreTranslateMessage(pMsg);
2491 CString CGitProgressList::GetPathFromColumnText(const CString& sColumnText)
2493 CString sPath = sColumnText;
2494 if (sPath.Find(':')<0)
2496 // the path is not absolute: add the common root of all paths to it
2497 sPath = g_Git.m_CurrentDir + _T("\\") + sColumnText;
2499 return sPath;