GitProgressDlg: Make taskbar progressbar work again
[TortoiseGit.git] / src / TortoiseProc / GitProgressList.cpp
blob1e07868b40d6c03ab7fbc1ed565b48f1a3413197
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"
48 #include "LoglistUtils.h"
50 static UINT WM_GITPROGRESS = RegisterWindowMessage(_T("TORTOISEGIT_GITPROGRESS_MSG"));
52 BOOL CGitProgressList::m_bAscending = FALSE;
53 int CGitProgressList::m_nSortedColumn = -1;
55 #define TRANSFERTIMER 100
56 #define VISIBLETIMER 101
57 // CGitProgressList
59 enum GITProgressDlgContextMenuCommands
61 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
62 ID_COMPARE = 1,
63 ID_EDITCONFLICT,
64 ID_CONFLICTRESOLVE,
65 ID_CONFLICTUSETHEIRS,
66 ID_CONFLICTUSEMINE,
67 ID_LOG,
68 ID_OPEN,
69 ID_OPENWITH,
70 ID_EXPLORE,
71 ID_COPY
74 IMPLEMENT_DYNAMIC(CGitProgressList, CListCtrl)
76 CGitProgressList::CGitProgressList():CListCtrl()
77 , m_bCancelled(FALSE)
78 , m_pThread(NULL)
79 , m_bErrorsOccurred(false)
80 , m_bBare(false)
81 , m_bNoCheckout(false)
82 , m_AutoTag(GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
83 , m_options(ProgOptNone)
84 , m_bSetTitle(false)
85 , m_pTaskbarList(nullptr)
87 m_pInfoCtrl = nullptr;
88 m_pAnimate = nullptr;
89 m_pProgControl = nullptr;
90 m_pProgressLabelCtrl = nullptr;
91 m_pPostWnd = nullptr;
94 CGitProgressList::~CGitProgressList()
96 for (size_t i = 0; i < m_arData.size(); ++i)
98 delete m_arData[i];
100 if(m_pThread != NULL)
102 delete m_pThread;
107 BEGIN_MESSAGE_MAP(CGitProgressList, CListCtrl)
108 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawSvnprogress)
109 ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkSvnprogress)
110 ON_NOTIFY_REFLECT(HDN_ITEMCLICK, OnHdnItemclickSvnprogress)
111 ON_REGISTERED_MESSAGE(WM_GITPROGRESS, OnGitProgress)
112 ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnLvnBegindragSvnprogress)
113 ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoSvnprogress)
114 ON_MESSAGE(WM_SHOWCONFLICTRESOLVER, OnShowConflictResolver)
115 ON_WM_SIZE()
116 ON_WM_TIMER()
117 ON_WM_CONTEXTMENU()
118 ON_WM_CLOSE()
119 END_MESSAGE_MAP()
121 void CGitProgressList::Cancel()
123 m_bCancelled = TRUE;
128 // CGitProgressList message handlers
131 LRESULT CGitProgressList::OnShowConflictResolver(WPARAM /*wParam*/, LPARAM /*lParam*/)
133 #if 0
134 CConflictResolveDlg dlg(this);
135 const svn_wc_conflict_description_t *description = (svn_wc_conflict_description_t *)lParam;
136 if (description)
138 dlg.SetConflictDescription(description);
139 if (m_pTaskbarList)
141 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_PAUSED);
143 if (dlg.DoModal() == IDOK)
145 if (dlg.GetResult() == svn_wc_conflict_choose_postpone)
147 // if the result is conflicted and the dialog returned IDOK,
148 // that means we should not ask again in case of a conflict
149 m_AlwaysConflicted = true;
150 ::SendMessage(GetDlgItem(IDC_NONINTERACTIVE)->GetSafeHwnd(), BM_SETCHECK, BST_CHECKED, 0);
153 m_mergedfile = dlg.GetMergedFile();
154 m_bCancelled = dlg.IsCancelled();
155 if (m_pTaskbarList)
156 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_INDETERMINATE);
157 return dlg.GetResult();
160 return svn_wc_conflict_choose_postpone;
161 #endif
162 return 0;
164 #if 0
165 svn_wc_conflict_choice_t CGitProgressList::ConflictResolveCallback(const svn_wc_conflict_description_t *description, CString& mergedfile)
167 // we only bother the user when merging
168 if (((m_Command == GitProgress_Merge)||(m_Command == GitProgress_MergeAll)||(m_Command == GitProgress_MergeReintegrate))&&(!m_AlwaysConflicted)&&(description))
170 // we're in a worker thread here. That means we must not show a dialog from the thread
171 // but let the UI thread do it.
172 // To do that, we send a message to the UI thread and let it show the conflict resolver dialog.
173 LRESULT dlgResult = ::SendMessage(GetSafeHwnd(), WM_SHOWCONFLICTRESOLVER, 0, (LPARAM)description);
174 mergedfile = m_mergedfile;
175 return (svn_wc_conflict_choice_t)dlgResult;
178 return svn_wc_conflict_choose_postpone;
180 #endif
181 void CGitProgressList::AddItemToList()
183 int totalcount = GetItemCount();
185 SetItemCountEx(totalcount+1, LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
186 // make columns width fit
187 if (iFirstResized < 30)
189 // only resize the columns for the first 30 or so entries.
190 // after that, don't resize them anymore because that's an
191 // expensive function call and the columns will be sized
192 // close enough already.
193 ResizeColumns();
194 ++iFirstResized;
197 // Make sure the item is *entirely* visible even if the horizontal
198 // scroll bar is visible.
199 int count = GetCountPerPage();
200 if (totalcount <= (GetTopIndex() + count + nEnsureVisibleCount + 2))
202 ++nEnsureVisibleCount;
203 m_bLastVisible = true;
205 else
207 nEnsureVisibleCount = 0;
208 if (IsIconic() == 0)
209 m_bLastVisible = false;
214 BOOL CGitProgressList::Notify(const CTGitPath& path, git_wc_notify_action_t action,
215 int /*status*/ ,
216 CString *strErr
218 svn_node_kind_t kind, const CString& mime_type,
219 svn_wc_notify_state_t content_state,
220 svn_wc_notify_state_t prop_state, LONG rev,
221 const svn_lock_t * lock, svn_wc_notify_lock_state_t lock_state,
222 const CString& changelistname,
223 svn_merge_range_t * range,
224 svn_error_t * err, apr_pool_t * pool
227 bool bNoNotify = false;
228 bool bDoAddData = true;
229 NotificationData * data = new NotificationData();
230 data->path = path;
231 data->action = action;
232 data->sPathColumnText=path.GetGitPathString();
233 data->bAuxItem = false;
235 if (this->m_pAnimate)
236 this->m_pAnimate->ShowWindow(SW_HIDE);
238 #if 0
239 data->kind = kind;
240 data->mime_type = mime_type;
241 data->content_state = content_state;
242 data->prop_state = prop_state;
243 data->rev = rev;
244 data->lock_state = lock_state;
245 data->changelistname = changelistname;
246 if ((lock)&&(lock->owner))
247 data->owner = CUnicodeUtils::GetUnicode(lock->owner);
248 data->sPathColumnText = path.GetUIPathString();
249 if (!m_basePath.IsEmpty())
250 data->basepath = m_basePath;
251 if (range)
252 data->merge_range = *range;
253 #endif
254 switch (data->action)
256 case git_wc_notify_add:
257 //case svn_wc_notify_update_add:
258 // if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
259 // {
260 // data->color = m_Colors.GetColor(CColors::Conflict);
261 // data->bConflictedActionItem = true;
262 // data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
263 // ++m_nConflicts;
264 // }
265 // else
266 // {
267 // m_bMergesAddsDeletesOccurred = true;
268 data->sActionColumnText.LoadString(IDS_SVNACTION_ADD);
269 data->color = m_Colors.GetColor(CColors::Added);
270 // }
271 break;
272 case git_wc_notify_sendmail_start:
273 data->bAuxItem = true;
274 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_START);
275 data->color = m_Colors.GetColor(CColors::Modified);
276 break;
278 case git_wc_notify_sendmail_error:
279 data->bAuxItem = true;
280 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_ERROR);
281 if(strErr)
282 data->sPathColumnText = *strErr;
283 else
284 data->sPathColumnText.Empty();
285 data->color = m_Colors.GetColor(CColors::Modified);
286 break;
288 case git_wc_notify_sendmail_done:
290 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_DONE);
291 data->sPathColumnText.Empty();
292 data->color = m_Colors.GetColor(CColors::Modified);
293 break;
295 case git_wc_notify_sendmail_retry:
296 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_RETRY);
297 data->sPathColumnText.Empty();
298 data->color = m_Colors.GetColor(CColors::Modified);
299 break;
302 case git_wc_notify_resolved:
303 data->sActionColumnText.LoadString(IDS_SVNACTION_RESOLVE);
304 break;
306 case git_wc_notify_revert:
307 data->sActionColumnText.LoadString(IDS_SVNACTION_REVERT);
308 break;
310 case git_wc_notify_checkout:
311 data->sActionColumnText.LoadString(IDS_PROGRS_CMD_CHECKOUT);
312 data->color = m_Colors.GetColor(CColors::Added);
313 data->bAuxItem = false;
314 break;
316 #if 0
317 case svn_wc_notify_commit_added:
318 data->sActionColumnText.LoadString(IDS_SVNACTION_ADDING);
319 data->color = m_Colors.GetColor(CColors::Added);
320 break;
321 case svn_wc_notify_copy:
322 data->sActionColumnText.LoadString(IDS_SVNACTION_COPY);
323 break;
324 case svn_wc_notify_commit_modified:
325 data->sActionColumnText.LoadString(IDS_SVNACTION_MODIFIED);
326 data->color = m_Colors.GetColor(CColors::Modified);
327 break;
328 case svn_wc_notify_delete:
329 case svn_wc_notify_update_delete:
330 data->sActionColumnText.LoadString(IDS_SVNACTION_DELETE);
331 m_bMergesAddsDeletesOccurred = true;
332 data->color = m_Colors.GetColor(CColors::Deleted);
333 break;
334 case svn_wc_notify_commit_deleted:
335 data->sActionColumnText.LoadString(IDS_SVNACTION_DELETING);
336 data->color = m_Colors.GetColor(CColors::Deleted);
337 break;
338 case svn_wc_notify_restore:
339 data->sActionColumnText.LoadString(IDS_SVNACTION_RESTORE);
340 break;
342 case svn_wc_notify_update_replace:
343 case svn_wc_notify_commit_replaced:
344 data->sActionColumnText.LoadString(IDS_SVNACTION_REPLACED);
345 data->color = m_Colors.GetColor(CColors::Deleted);
346 break;
347 case svn_wc_notify_exists:
348 if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
350 data->color = m_Colors.GetColor(CColors::Conflict);
351 data->bConflictedActionItem = true;
352 ++m_nConflicts;
353 data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
355 else if ((data->content_state == svn_wc_notify_state_merged) || (data->prop_state == svn_wc_notify_state_merged))
357 data->color = m_Colors.GetColor(CColors::Merged);
358 m_bMergesAddsDeletesOccurred = true;
359 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGED);
361 else
362 data->sActionColumnText.LoadString(IDS_SVNACTION_EXISTS);
363 break;
364 case svn_wc_notify_update_update:
365 // if this is an inoperative dir change, don't show the notification.
366 // an inoperative dir change is when a directory gets updated without
367 // any real change in either text or properties.
368 if ((kind == svn_node_dir)
369 && ((prop_state == svn_wc_notify_state_inapplicable)
370 || (prop_state == svn_wc_notify_state_unknown)
371 || (prop_state == svn_wc_notify_state_unchanged)))
373 bNoNotify = true;
374 break;
376 if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
378 data->color = m_Colors.GetColor(CColors::Conflict);
379 data->bConflictedActionItem = true;
380 ++m_nConflicts;
381 data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
383 else if ((data->content_state == svn_wc_notify_state_merged) || (data->prop_state == svn_wc_notify_state_merged))
385 data->color = m_Colors.GetColor(CColors::Merged);
386 m_bMergesAddsDeletesOccurred = true;
387 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGED);
389 else if (((data->content_state != svn_wc_notify_state_unchanged)&&(data->content_state != svn_wc_notify_state_unknown)) ||
390 ((data->prop_state != svn_wc_notify_state_unchanged)&&(data->prop_state != svn_wc_notify_state_unknown)))
392 data->sActionColumnText.LoadString(IDS_SVNACTION_UPDATE);
394 else
396 bNoNotify = true;
397 break;
399 if (lock_state == svn_wc_notify_lock_state_unlocked)
401 CString temp(MAKEINTRESOURCE(IDS_SVNACTION_UNLOCKED));
402 data->sActionColumnText += _T(", ") + temp;
404 break;
406 case svn_wc_notify_update_external:
407 // For some reason we build a list of externals...
408 m_ExtStack.AddHead(path.GetUIPathString());
409 data->sActionColumnText.LoadString(IDS_SVNACTION_EXTERNAL);
410 data->bAuxItem = true;
411 break;
413 case svn_wc_notify_update_completed:
415 data->sActionColumnText.LoadString(IDS_SVNACTION_COMPLETED);
416 data->bAuxItem = true;
417 bool bEmpty = !!m_ExtStack.IsEmpty();
418 if (!bEmpty)
419 data->sPathColumnText.Format(IDS_PROGRS_PATHATREV, (LPCTSTR)m_ExtStack.RemoveHead(), rev);
420 else
421 data->sPathColumnText.Format(IDS_PROGRS_ATREV, rev);
423 if ((m_nConflicts>0)&&(bEmpty))
425 // We're going to add another aux item - let's shove this current onto the list first
426 // I don't really like this, but it will do for the moment.
427 m_arData.push_back(data);
428 AddItemToList();
430 data = new NotificationData();
431 data->bAuxItem = true;
432 data->sActionColumnText.LoadString(IDS_PROGRS_CONFLICTSOCCURED_WARNING);
433 data->sPathColumnText.LoadString(IDS_PROGRS_CONFLICTSOCCURED);
434 data->color = m_Colors.GetColor(CColors::Conflict);
435 CSoundUtils::PlayTSVNWarning();
436 // This item will now be added after the switch statement
438 if (!m_basePath.IsEmpty())
439 m_FinishedRevMap[m_basePath.GetSVNApiPath(pool)] = rev;
440 m_RevisionEnd = rev;
441 m_bFinishedItemAdded = true;
443 break;
444 case svn_wc_notify_commit_postfix_txdelta:
445 data->sActionColumnText.LoadString(IDS_SVNACTION_POSTFIX);
446 break;
447 case svn_wc_notify_failed_revert:
448 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDREVERT);
449 break;
450 case svn_wc_notify_status_completed:
451 case svn_wc_notify_status_external:
452 data->sActionColumnText.LoadString(IDS_SVNACTION_STATUS);
453 break;
454 case svn_wc_notify_skip:
455 if ((content_state == svn_wc_notify_state_missing)||(content_state == svn_wc_notify_state_obstructed)||(content_state == svn_wc_notify_state_conflicted))
457 data->sActionColumnText.LoadString(IDS_SVNACTION_SKIPMISSING);
459 // The color settings dialog describes the red color with
460 // "possible or real conflict / obstructed" which also applies to
461 // skipped targets during a merge. So we just use the same color.
462 data->color = m_Colors.GetColor(CColors::Conflict);
464 else
465 data->sActionColumnText.LoadString(IDS_SVNACTION_SKIP);
466 break;
467 case svn_wc_notify_locked:
468 if ((lock)&&(lock->owner))
469 data->sActionColumnText.Format(IDS_SVNACTION_LOCKEDBY, (LPCTSTR)CUnicodeUtils::GetUnicode(lock->owner));
470 break;
471 case svn_wc_notify_unlocked:
472 data->sActionColumnText.LoadString(IDS_SVNACTION_UNLOCKED);
473 break;
474 case svn_wc_notify_failed_lock:
475 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDLOCK);
476 m_arData.push_back(data);
477 AddItemToList();
478 ReportError(SVN::GetErrorString(err));
479 bDoAddData = false;
480 if (err->apr_err == SVN_ERR_FS_OUT_OF_DATE)
481 m_bLockWarning = true;
482 if (err->apr_err == SVN_ERR_FS_PATH_ALREADY_LOCKED)
483 m_bLockExists = true;
484 break;
485 case svn_wc_notify_failed_unlock:
486 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDUNLOCK);
487 m_arData.push_back(data);
488 AddItemToList();
489 ReportError(SVN::GetErrorString(err));
490 bDoAddData = false;
491 if (err->apr_err == SVN_ERR_FS_OUT_OF_DATE)
492 m_bLockWarning = true;
493 break;
494 case svn_wc_notify_changelist_set:
495 data->sActionColumnText.Format(IDS_SVNACTION_CHANGELISTSET, (LPCTSTR)data->changelistname);
496 break;
497 case svn_wc_notify_changelist_clear:
498 data->sActionColumnText.LoadString(IDS_SVNACTION_CHANGELISTCLEAR);
499 break;
500 case svn_wc_notify_changelist_moved:
501 data->sActionColumnText.Format(IDS_SVNACTION_CHANGELISTMOVED, (LPCTSTR)data->changelistname);
502 break;
503 case svn_wc_notify_foreign_merge_begin:
504 case svn_wc_notify_merge_begin:
505 if (range == NULL)
506 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGEBEGINNONE);
507 else if ((data->merge_range.start == data->merge_range.end) || (data->merge_range.start == data->merge_range.end - 1))
508 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINSINGLE, data->merge_range.end);
509 else if (data->merge_range.start - 1 == data->merge_range.end)
510 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINSINGLEREVERSE, data->merge_range.start);
511 else if (data->merge_range.start < data->merge_range.end)
512 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINMULTIPLE, data->merge_range.start + 1, data->merge_range.end);
513 else
514 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINMULTIPLEREVERSE, data->merge_range.start, data->merge_range.end + 1);
515 data->bAuxItem = true;
516 break;
517 #endif
518 default:
519 break;
520 } // switch (data->action)
522 if (bNoNotify)
523 delete data;
524 else
526 if (bDoAddData)
528 m_arData.push_back(data);
529 AddItemToList();
530 if ((!data->bAuxItem) && (m_itemCount > 0))
532 if (m_pProgControl)
534 m_pProgControl->ShowWindow(SW_SHOW);
535 m_pProgControl->SetPos(m_itemCount);
536 m_pProgControl->SetRange32(0, m_itemCountTotal);
538 if (m_pTaskbarList && m_pPostWnd)
540 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_NORMAL);
541 m_pTaskbarList->SetProgressValue(m_pPostWnd->GetSafeHwnd(), m_itemCountTotal - m_itemCount, m_itemCountTotal);
542 m_pTaskbarList->SetProgressValue(m_pPostWnd->GetSafeHwnd(), m_itemCount, m_itemCountTotal);
546 //if ((action == svn_wc_notify_commit_postfix_txdelta)&&(bSecondResized == FALSE))
548 // ResizeColumns();
549 // bSecondResized = TRUE;
553 return TRUE;
557 CString CGitProgressList::BuildInfoString()
559 CString infotext;
560 if(this->m_Command == GitProgress_Resolve)
561 infotext = _T("You need commit your change after resolve conflict");
562 #if 0
564 CString temp;
565 int added = 0;
566 int copied = 0;
567 int deleted = 0;
568 int restored = 0;
569 int reverted = 0;
570 int resolved = 0;
571 int conflicted = 0;
572 int updated = 0;
573 int merged = 0;
574 int modified = 0;
575 int skipped = 0;
576 int replaced = 0;
578 for (size_t i=0; i<m_arData.size(); ++i)
580 const NotificationData * dat = m_arData[i];
581 switch (dat->action)
583 case svn_wc_notify_add:
584 case svn_wc_notify_update_add:
585 case svn_wc_notify_commit_added:
586 if (dat->bConflictedActionItem)
587 ++conflicted;
588 else
589 ++added;
590 break;
591 case svn_wc_notify_copy:
592 ++copied;
593 break;
594 case svn_wc_notify_delete:
595 case svn_wc_notify_update_delete:
596 case svn_wc_notify_commit_deleted:
597 ++deleted;
598 break;
599 case svn_wc_notify_restore:
600 ++restored;
601 break;
602 case svn_wc_notify_revert:
603 ++reverted;
604 break;
605 case svn_wc_notify_resolved:
606 ++resolved;
607 break;
608 case svn_wc_notify_update_update:
609 if (dat->bConflictedActionItem)
610 ++conflicted;
611 else if ((dat->content_state == svn_wc_notify_state_merged) || (dat->prop_state == svn_wc_notify_state_merged))
612 ++merged;
613 else
614 ++updated;
615 break;
616 case svn_wc_notify_commit_modified:
617 ++modified;
618 break;
619 case svn_wc_notify_skip:
620 ++skipped;
621 break;
622 case svn_wc_notify_commit_replaced:
623 ++replaced;
624 break;
627 if (conflicted)
629 temp.LoadString(IDS_SVNACTION_CONFLICTED);
630 infotext += temp;
631 temp.Format(_T(":%d "), conflicted);
632 infotext += temp;
634 if (skipped)
636 temp.LoadString(IDS_SVNACTION_SKIP);
637 infotext += temp;
638 infotext.AppendFormat(_T(":%d "), skipped);
640 if (merged)
642 temp.LoadString(IDS_SVNACTION_MERGED);
643 infotext += temp;
644 infotext.AppendFormat(_T(":%d "), merged);
646 if (added)
648 temp.LoadString(IDS_SVNACTION_ADD);
649 infotext += temp;
650 infotext.AppendFormat(_T(":%d "), added);
652 if (deleted)
654 temp.LoadString(IDS_SVNACTION_DELETE);
655 infotext += temp;
656 infotext.AppendFormat(_T(":%d "), deleted);
658 if (modified)
660 temp.LoadString(IDS_SVNACTION_MODIFIED);
661 infotext += temp;
662 infotext.AppendFormat(_T(":%d "), modified);
664 if (copied)
666 temp.LoadString(IDS_SVNACTION_COPY);
667 infotext += temp;
668 infotext.AppendFormat(_T(":%d "), copied);
670 if (replaced)
672 temp.LoadString(IDS_SVNACTION_REPLACED);
673 infotext += temp;
674 infotext.AppendFormat(_T(":%d "), replaced);
676 if (updated)
678 temp.LoadString(IDS_SVNACTION_UPDATE);
679 infotext += temp;
680 infotext.AppendFormat(_T(":%d "), updated);
682 if (restored)
684 temp.LoadString(IDS_SVNACTION_RESTORE);
685 infotext += temp;
686 infotext.AppendFormat(_T(":%d "), restored);
688 if (reverted)
690 temp.LoadString(IDS_SVNACTION_REVERT);
691 infotext += temp;
692 infotext.AppendFormat(_T(":%d "), reverted);
694 if (resolved)
696 temp.LoadString(IDS_SVNACTION_RESOLVE);
697 infotext += temp;
698 infotext.AppendFormat(_T(":%d "), resolved);
700 #endif
701 return infotext;
704 void CGitProgressList::SetSelectedList(const CTGitPathList& selPaths)
706 m_selectedPaths = selPaths;
709 void CGitProgressList::ResizeColumns()
711 SetRedraw(FALSE);
713 TCHAR textbuf[MAX_PATH];
715 int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
716 for (int col = 0; col <= maxcol; ++col)
718 // find the longest width of all items
719 int count = GetItemCount();
720 HDITEM hdi = {0};
721 hdi.mask = HDI_TEXT;
722 hdi.pszText = textbuf;
723 hdi.cchTextMax = sizeof(textbuf);
724 ((CHeaderCtrl*)(GetDlgItem(0)))->GetItem(col, &hdi);
725 int cx = GetStringWidth(hdi.pszText)+20; // 20 pixels for col separator and margin
727 for (int index = 0; index<count; ++index)
729 // get the width of the string and add 12 pixels for the column separator and margins
730 int linewidth = cx;
731 switch (col)
733 case 0:
734 linewidth = GetStringWidth(m_arData[index]->sActionColumnText) + 12;
735 break;
736 case 1:
737 linewidth = GetStringWidth(m_arData[index]->sPathColumnText) + 12;
738 break;
740 if (cx < linewidth)
741 cx = linewidth;
743 SetColumnWidth(col, cx);
746 SetRedraw(TRUE);
749 bool CGitProgressList::SetBackgroundImage(UINT nID)
751 return CAppUtils::SetListCtrlBackgroundImage(GetSafeHwnd(), nID);
754 void CGitProgressList::ReportGitError()
756 ReportError(CGit::GetLibGit2LastErr());
759 void CGitProgressList::ReportError(const CString& sError)
761 CSoundUtils::PlayTGitError();
762 ReportString(sError, CString(MAKEINTRESOURCE(IDS_ERR_ERROR)), m_Colors.GetColor(CColors::Conflict));
763 m_bErrorsOccurred = true;
766 void CGitProgressList::ReportWarning(const CString& sWarning)
768 CSoundUtils::PlayTGitWarning();
769 ReportString(sWarning, CString(MAKEINTRESOURCE(IDS_WARN_WARNING)), m_Colors.GetColor(CColors::Conflict));
772 void CGitProgressList::ReportNotification(const CString& sNotification)
774 CSoundUtils::PlayTGitNotification();
775 ReportString(sNotification, CString(MAKEINTRESOURCE(IDS_WARN_NOTE)));
778 void CGitProgressList::ReportCmd(const CString& sCmd)
780 ReportString(sCmd, CString(MAKEINTRESOURCE(IDS_PROGRS_CMDINFO)), m_Colors.GetColor(CColors::Cmd));
783 void CGitProgressList::ReportString(CString sMessage, const CString& sMsgKind, COLORREF color)
785 // instead of showing a dialog box with the error message or notification,
786 // just insert the error text into the list control.
787 // that way the user isn't 'interrupted' by a dialog box popping up!
789 // the message may be split up into different lines
790 // so add a new entry for each line of the message
791 while (!sMessage.IsEmpty())
793 NotificationData * data = new NotificationData();
794 data->bAuxItem = true;
795 data->sActionColumnText = sMsgKind;
796 if (sMessage.Find('\n')>=0)
797 data->sPathColumnText = sMessage.Left(sMessage.Find('\n'));
798 else
799 data->sPathColumnText = sMessage;
800 data->sPathColumnText.Trim(_T("\n\r"));
801 data->color = color;
802 if (sMessage.Find('\n')>=0)
804 sMessage = sMessage.Mid(sMessage.Find('\n'));
805 sMessage.Trim(_T("\n\r"));
807 else
808 sMessage.Empty();
809 m_arData.push_back(data);
810 AddItemToList();
814 UINT CGitProgressList::ProgressThreadEntry(LPVOID pVoid)
816 return ((CGitProgressList*)pVoid)->ProgressThread();
819 UINT CGitProgressList::ProgressThread()
821 // The SetParams function should have loaded something for us
823 CString temp;
824 CString sWindowTitle;
825 bool localoperation = false;
826 bool bSuccess = false;
827 m_AlwaysConflicted = false;
829 if(m_pPostWnd)
830 m_pPostWnd->PostMessage(WM_PROG_CMD_START, m_Command);
832 if(m_pProgressLabelCtrl)
834 m_pProgressLabelCtrl->ShowWindow(SW_SHOW);
835 m_pProgressLabelCtrl->SetWindowText(_T(""));
838 // SetAndClearProgressInfo(m_hWnd);
839 m_itemCount = m_itemCountTotal;
841 InterlockedExchange(&m_bThreadRunning, TRUE);
842 iFirstResized = 0;
843 bSecondResized = FALSE;
844 m_bFinishedItemAdded = false;
845 DWORD startTime = GetCurrentTime();
847 if (m_pTaskbarList && m_pPostWnd)
848 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_INDETERMINATE);
850 switch (m_Command)
852 case GitProgress_Add:
853 bSuccess = CmdAdd(sWindowTitle, localoperation);
854 break;
855 case GitProgress_Copy:
856 bSuccess = CmdCopy(sWindowTitle, localoperation);
857 break;
858 case GitProgress_Export:
859 bSuccess = CmdExport(sWindowTitle, localoperation);
860 break;
861 case GitProgress_Rename:
862 bSuccess = CmdRename(sWindowTitle, localoperation);
863 break;
864 case GitProgress_Resolve:
865 bSuccess = CmdResolve(sWindowTitle, localoperation);
866 break;
867 case GitProgress_Revert:
868 bSuccess = CmdRevert(sWindowTitle, localoperation);
869 break;
870 case GitProgress_Switch:
871 bSuccess = CmdSwitch(sWindowTitle, localoperation);
872 break;
873 case GitProgress_SendMail:
874 bSuccess = CmdSendMail(sWindowTitle, localoperation);
875 break;
876 case GitProgress_Clone:
877 bSuccess = CmdClone(sWindowTitle, localoperation);
878 break;
879 case GitProgress_Fetch:
880 bSuccess = CmdFetch(sWindowTitle, localoperation);
881 break;
883 if (!bSuccess)
884 temp.LoadString(IDS_PROGRS_TITLEFAILED);
885 else
886 temp.LoadString(IDS_PROGRS_TITLEFIN);
887 sWindowTitle = sWindowTitle + _T(" ") + temp;
888 if (m_bSetTitle && m_pPostWnd)
889 ::SetWindowText(m_pPostWnd->GetSafeHwnd(), sWindowTitle);
891 KillTimer(TRANSFERTIMER);
892 KillTimer(VISIBLETIMER);
894 if (m_pTaskbarList && m_pPostWnd)
896 if (DidErrorsOccur())
898 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_ERROR);
899 m_pTaskbarList->SetProgressValue(m_pPostWnd->GetSafeHwnd(), 100, 100);
901 else
902 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_NOPROGRESS);
905 CString info = BuildInfoString();
906 if (!bSuccess)
907 info.LoadString(IDS_PROGRS_INFOFAILED);
908 if (m_pInfoCtrl)
909 m_pInfoCtrl->SetWindowText(info);
911 ResizeColumns();
913 DWORD time = GetCurrentTime() - startTime;
915 CString sFinalInfo;
916 if (!m_sTotalBytesTransferred.IsEmpty())
918 temp.Format(IDS_PROGRS_TIME, (time / 1000) / 60, (time / 1000) % 60);
919 sFinalInfo.Format(IDS_PROGRS_FINALINFO, m_sTotalBytesTransferred, (LPCTSTR)temp);
920 if (m_pProgressLabelCtrl)
921 m_pProgressLabelCtrl->SetWindowText(sFinalInfo);
923 else
925 if (m_pProgressLabelCtrl)
926 m_pProgressLabelCtrl->ShowWindow(SW_HIDE);
929 if (m_pProgControl)
930 m_pProgControl->ShowWindow(SW_HIDE);
932 if (!m_bFinishedItemAdded)
934 CString log, str;
935 if (bSuccess)
936 str.LoadString(IDS_SUCCESS);
937 else
938 str.Format(IDS_PROC_PROGRESS_GITUNCLEANEXIT, !bSuccess);
939 log.Format(_T("%s (%d ms @ %s)"), str, time, CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false));
941 // there's no "finished: xxx" line at the end. We add one here to make
942 // sure the user sees that the command is actually finished.
943 ReportString(log, CString(MAKEINTRESOURCE(IDS_PROGRS_FINISHED)), bSuccess? RGB(0,0,255) : RGB(255,0,0));
946 int count = GetItemCount();
947 if ((count > 0)&&(m_bLastVisible))
948 EnsureVisible(count-1, FALSE);
950 CLogFile logfile;
951 if (logfile.Open())
953 logfile.AddTimeLine();
954 for (size_t i = 0; i < m_arData.size(); ++i)
956 NotificationData * data = m_arData[i];
957 temp.Format(_T("%-20s : %s"), (LPCTSTR)data->sActionColumnText, (LPCTSTR)data->sPathColumnText);
958 logfile.AddLine(temp);
960 if (!sFinalInfo.IsEmpty())
961 logfile.AddLine(sFinalInfo);
962 logfile.Close();
965 m_bCancelled = TRUE;
966 InterlockedExchange(&m_bThreadRunning, FALSE);
967 #if 0 //need
968 RefreshCursor();
969 #endif
971 if (m_pPostWnd)
972 m_pPostWnd->PostMessage(WM_PROG_CMD_FINISH, this->m_Command, 0L);
974 #if 0
975 DWORD dwAutoClose = CRegStdDWORD(_T("Software\\TortoiseGit\\AutoClose"), CLOSE_MANUAL);
976 if (m_options & ProgOptDryRun)
977 dwAutoClose = 0; // dry run means progress dialog doesn't auto close at all
978 if (!m_bLastVisible)
979 dwAutoClose = 0;
980 if (m_dwCloseOnEnd != (DWORD)-1)
981 dwAutoClose = m_dwCloseOnEnd; // command line value has priority over setting value
982 if ((dwAutoClose == CLOSE_NOERRORS)&&(!m_bErrorsOccurred))
983 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
984 if ((dwAutoClose == CLOSE_NOCONFLICTS)&&(!m_bErrorsOccurred)&&(m_nConflicts==0))
985 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
986 if ((dwAutoClose == CLOSE_NOMERGES)&&(!m_bErrorsOccurred)&&(m_nConflicts==0)&&(!m_bMergesAddsDeletesOccurred))
987 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
988 if ((dwAutoClose == CLOSE_LOCAL)&&(!m_bErrorsOccurred)&&(m_nConflicts==0)&&(localoperation))
989 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
990 #endif
992 //Don't do anything here which might cause messages to be sent to the window
993 //The window thread is probably now blocked in OnOK if we've done an auto close
994 return 0;
997 void CGitProgressList::OnLvnGetdispinfoSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
999 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
1001 if (pDispInfo)
1003 if (pDispInfo->item.mask & LVIF_TEXT)
1005 if (pDispInfo->item.iItem < (int)m_arData.size())
1007 const NotificationData * data = m_arData[pDispInfo->item.iItem];
1008 switch (pDispInfo->item.iSubItem)
1010 case 0:
1011 lstrcpyn(m_columnbuf, data->sActionColumnText, MAX_PATH);
1012 break;
1013 case 1:
1014 lstrcpyn(m_columnbuf, data->sPathColumnText, pDispInfo->item.cchTextMax);
1015 if (!data->bAuxItem)
1017 int cWidth = GetColumnWidth(1);
1018 cWidth = max(12, cWidth-12);
1019 CDC * pDC = GetDC();
1020 if (pDC != NULL)
1022 CFont * pFont = pDC->SelectObject(GetFont());
1023 PathCompactPath(pDC->GetSafeHdc(), m_columnbuf, cWidth);
1024 pDC->SelectObject(pFont);
1025 ReleaseDC(pDC);
1028 break;
1029 default:
1030 m_columnbuf[0] = 0;
1032 pDispInfo->item.pszText = m_columnbuf;
1036 *pResult = 0;
1039 void CGitProgressList::OnNMCustomdrawSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
1041 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
1043 // Take the default processing unless we set this to something else below.
1044 *pResult = CDRF_DODEFAULT;
1046 // First thing - check the draw stage. If it's the control's prepaint
1047 // stage, then tell Windows we want messages for every item.
1049 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
1051 *pResult = CDRF_NOTIFYITEMDRAW;
1053 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
1055 // This is the prepaint stage for an item. Here's where we set the
1056 // item's text color. Our return value will tell Windows to draw the
1057 // item itself, but it will use the new color we set here.
1059 // Tell Windows to paint the control itself.
1060 *pResult = CDRF_DODEFAULT;
1062 ASSERT(pLVCD->nmcd.dwItemSpec < m_arData.size());
1063 if(pLVCD->nmcd.dwItemSpec >= m_arData.size())
1065 return;
1067 const NotificationData * data = m_arData[pLVCD->nmcd.dwItemSpec];
1068 ASSERT(data != NULL);
1069 if (data == NULL)
1070 return;
1072 // Store the color back in the NMLVCUSTOMDRAW struct.
1073 pLVCD->clrText = data->color;
1077 void CGitProgressList::OnNMDblclkSvnprogress(NMHDR * /*pNMHDR*/, LRESULT * /*pResult*/)
1079 #if 0
1080 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1081 *pResult = 0;
1082 if (pNMLV->iItem < 0)
1083 return;
1084 if (m_options & ProgOptDryRun)
1085 return; //don't do anything in a dry-run.
1087 const NotificationData * data = m_arData[pNMLV->iItem];
1088 if (data == NULL)
1089 return;
1091 if (data->bConflictedActionItem)
1093 // We've double-clicked on a conflicted item - do a three-way merge on it
1094 SVNDiff::StartConflictEditor(data->path);
1096 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))
1098 // This is a modified file which has been merged on update. Diff it against base
1099 CTGitPath temporaryFile;
1100 SVNDiff diff(this, this->m_hWnd, true);
1101 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1102 svn_revnum_t baseRev = 0;
1103 diff.DiffFileAgainstBase(data->path, baseRev);
1105 else if ((!data->bAuxItem)&&(data->path.Exists())&&(!data->path.IsDirectory()))
1107 bool bOpenWith = false;
1108 int ret = (int)ShellExecute(m_hWnd, NULL, data->path.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
1109 if (ret <= HINSTANCE_ERROR)
1110 bOpenWith = true;
1111 if (bOpenWith)
1113 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1114 cmd += data->path.GetWinPathString() + _T(" ");
1115 CAppUtils::LaunchApplication(cmd, NULL, false);
1118 #endif
1121 void CGitProgressList::OnHdnItemclickSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
1123 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
1124 if (m_bThreadRunning)
1125 return;
1126 if (m_nSortedColumn == phdr->iItem)
1127 m_bAscending = !m_bAscending;
1128 else
1129 m_bAscending = TRUE;
1130 m_nSortedColumn = phdr->iItem;
1131 Sort();
1133 CString temp;
1134 SetRedraw(FALSE);
1135 DeleteAllItems();
1136 SetItemCountEx (static_cast<int>(m_arData.size()));
1138 SetRedraw(TRUE);
1140 *pResult = 0;
1143 bool CGitProgressList::NotificationDataIsAux(const NotificationData* pData)
1145 return pData->bAuxItem;
1147 BOOL CGitProgressList::Notify(const git_wc_notify_action_t action, CString str, const git_oid *a, const git_oid *b)
1149 NotificationData * data = new NotificationData();
1150 data->action = action;
1151 data->bAuxItem = false;
1153 if (action == git_wc_notify_update_ref)
1155 data->m_NewHash = b->id;
1156 data->m_OldHash = a->id;
1157 data->sActionColumnText.LoadString(IDS_GITACTION_UPDATE_REF);
1158 data->sPathColumnText.Format(_T("%s\t %s -> %s"), str,
1159 data->m_OldHash.ToString().Left(g_Git.GetShortHASHLength()),
1160 data->m_NewHash.ToString().Left(g_Git.GetShortHASHLength()));
1164 m_arData.push_back(data);
1165 AddItemToList();
1167 if (m_pAnimate)
1168 m_pAnimate->Stop();
1169 if (m_pAnimate)
1170 m_pAnimate->ShowWindow(SW_HIDE);
1171 return TRUE;
1173 BOOL CGitProgressList::Notify(const git_wc_notify_action_t /*action*/, const git_transfer_progress *stat)
1175 static unsigned int start = 0;
1176 unsigned int dt = GetCurrentTime() - start;
1177 size_t ds;
1178 double speed = 0;
1180 if (m_bCancelled)
1181 return FALSE;
1183 if (dt > 100)
1185 start = GetCurrentTime();
1186 ds = stat->received_bytes - m_TotalBytesTransferred;
1187 speed = ds * 1000.0/dt;
1188 m_TotalBytesTransferred = stat->received_bytes;
1190 else
1192 return TRUE;
1195 int progress;
1196 progress = stat->received_objects + stat->indexed_objects;
1198 if ((stat->total_objects > 1000) && m_pProgControl && (!m_pProgControl->IsWindowVisible()))
1200 if (m_pProgControl)
1201 m_pProgControl->ShowWindow(SW_SHOW);
1202 if (m_pTaskbarList && m_pPostWnd)
1203 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_NORMAL);
1206 if (m_pProgressLabelCtrl && m_pProgressLabelCtrl->IsWindowVisible())
1207 m_pProgressLabelCtrl->ShowWindow(SW_SHOW);
1209 if (m_pProgControl)
1211 m_pProgControl->SetPos(progress);
1212 m_pProgControl->SetRange32(0, 2 * stat->total_objects);
1214 if (m_pTaskbarList && m_pPostWnd)
1216 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_NORMAL);
1217 m_pTaskbarList->SetProgressValue(m_pPostWnd->GetSafeHwnd(), progress, stat->total_objects);
1220 CString progText;
1221 if (stat->received_bytes < 1024)
1222 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALBYTESTRANSFERRED, (int64_t)stat->received_bytes);
1223 else if (stat->received_bytes < 1200000)
1224 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALTRANSFERRED, (int64_t)stat->received_bytes / 1024);
1225 else
1226 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALMBTRANSFERRED, (double)((double)stat->received_bytes / 1024000.0));
1228 CString str;
1229 if(speed < 1024)
1230 str.Format(_T("%fB/s"), speed);
1231 else if(speed < 1024 * 1024)
1232 str.Format(_T("%.2fKB/s"), speed / 1024);
1233 else
1234 str.Format(_T("%.2fMB/s"), speed / 1024000.0);
1236 progText.Format(IDS_SVN_PROGRESS_TOTALANDSPEED, (LPCTSTR)m_sTotalBytesTransferred, (LPCTSTR)str);
1237 if (m_pProgressLabelCtrl)
1238 m_pProgressLabelCtrl->SetWindowText(progText);
1240 return TRUE;
1243 LRESULT CGitProgressList::OnGitProgress(WPARAM /*wParam*/, LPARAM /*lParam*/)
1245 #if 0
1246 SVNProgress * pProgressData = (SVNProgress *)lParam;
1247 CProgressCtrl * progControl = (CProgressCtrl *)GetDlgItem(IDC_PROGRESSBAR);
1248 if ((pProgressData->total > 1000)&&(!progControl->IsWindowVisible()))
1250 progControl->ShowWindow(SW_SHOW);
1251 if (m_pTaskbarList)
1252 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1254 if (((pProgressData->total < 0)&&(pProgressData->progress > 1000)&&(progControl->IsWindowVisible()))&&(m_itemCountTotal<0))
1256 progControl->ShowWindow(SW_HIDE);
1257 if (m_pTaskbarList)
1258 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_INDETERMINATE);
1260 if (!GetDlgItem(IDC_PROGRESSLABEL)->IsWindowVisible())
1261 GetDlgItem(IDC_PROGRESSLABEL)->ShowWindow(SW_SHOW);
1262 SetTimer(TRANSFERTIMER, 2000, NULL);
1263 if ((pProgressData->total > 0)&&(pProgressData->progress > 1000))
1265 progControl->SetPos((int)pProgressData->progress);
1266 progControl->SetRange32(0, (int)pProgressData->total);
1267 if (m_pTaskbarList)
1269 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
1270 m_pTaskbarList->SetProgressValue(m_hWnd, pProgressData->progress, pProgressData->total);
1273 CString progText;
1274 if (pProgressData->overall_total < 1024)
1275 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALBYTESTRANSFERRED, pProgressData->overall_total);
1276 else if (pProgressData->overall_total < 1200000)
1277 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALTRANSFERRED, pProgressData->overall_total / 1024);
1278 else
1279 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALMBTRANSFERRED, (double)((double)pProgressData->overall_total / 1024000.0));
1280 progText.Format(IDS_SVN_PROGRESS_TOTALANDSPEED, (LPCTSTR)m_sTotalBytesTransferred, (LPCTSTR)pProgressData->SpeedString);
1281 SetDlgItemText(IDC_PROGRESSLABEL, progText);
1282 #endif
1283 return 0;
1286 void CGitProgressList::OnTimer(UINT_PTR nIDEvent)
1288 if (nIDEvent == TRANSFERTIMER)
1290 CString progText;
1291 CString progSpeed;
1292 progSpeed.Format(IDS_SVN_PROGRESS_BYTES_SEC, 0);
1293 progText.Format(IDS_SVN_PROGRESS_TOTALANDSPEED, (LPCTSTR)m_sTotalBytesTransferred, (LPCTSTR)progSpeed);
1294 if (m_pProgressLabelCtrl)
1295 m_pProgressLabelCtrl->SetWindowText(progText);
1297 KillTimer(TRANSFERTIMER);
1299 if (nIDEvent == VISIBLETIMER)
1301 if (nEnsureVisibleCount)
1302 EnsureVisible(GetItemCount()-1, false);
1303 nEnsureVisibleCount = 0;
1307 void CGitProgressList::Sort()
1309 if(m_arData.size() < 2)
1311 return;
1314 // We need to sort the blocks which lie between the auxiliary entries
1315 // This is so that any aux data stays where it was
1316 NotificationDataVect::iterator actionBlockBegin;
1317 NotificationDataVect::iterator actionBlockEnd = m_arData.begin(); // We start searching from here
1319 for(;;)
1321 // Search to the start of the non-aux entry in the next block
1322 actionBlockBegin = std::find_if(actionBlockEnd, m_arData.end(), std::not1(std::ptr_fun(&CGitProgressList::NotificationDataIsAux)));
1323 if(actionBlockBegin == m_arData.end())
1325 // There are no more actions
1326 break;
1328 // Now search to find the end of the block
1329 actionBlockEnd = std::find_if(actionBlockBegin+1, m_arData.end(), std::ptr_fun(&CGitProgressList::NotificationDataIsAux));
1330 // Now sort the block
1331 std::sort(actionBlockBegin, actionBlockEnd, &CGitProgressList::SortCompare);
1335 bool CGitProgressList::SortCompare(const NotificationData * pData1, const NotificationData * pData2)
1337 int result = 0;
1338 switch (m_nSortedColumn)
1340 case 0: //action column
1341 result = pData1->sActionColumnText.Compare(pData2->sActionColumnText);
1342 break;
1343 case 1: //path column
1344 // Compare happens after switch()
1345 break;
1346 default:
1347 break;
1350 // Sort by path if everything else is equal
1351 if (result == 0)
1353 result = CTGitPath::Compare(pData1->path, pData2->path);
1356 if (!m_bAscending)
1357 result = -result;
1358 return result < 0;
1361 void CGitProgressList::OnContextMenu(CWnd* pWnd, CPoint point)
1363 if (m_options & ProgOptDryRun)
1364 return; // don't do anything in a dry-run.
1366 if (pWnd == this)
1368 int selIndex = GetSelectionMark();
1369 if ((point.x == -1) && (point.y == -1))
1371 // Menu was invoked from the keyboard rather than by right-clicking
1372 CRect rect;
1373 GetItemRect(selIndex, &rect, LVIR_LABEL);
1374 ClientToScreen(&rect);
1375 point = rect.CenterPoint();
1378 if ((selIndex >= 0)&&(!m_bThreadRunning))
1380 // entry is selected, thread has finished with updating so show the popup menu
1381 CIconMenu popup;
1382 if (popup.CreatePopupMenu())
1384 bool bAdded = false;
1385 NotificationData * data = m_arData[selIndex];
1386 if ((data)&&(!data->path.IsDirectory()))
1389 if (data->action == svn_wc_notify_update_update || data->action == svn_wc_notify_resolved)
1391 if (GetSelectedCount() == 1)
1393 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
1394 bAdded = true;
1398 if (data->bConflictedActionItem)
1400 if (GetSelectedCount() == 1)
1402 popup.AppendMenuIcon(ID_EDITCONFLICT, IDS_MENUCONFLICT,IDI_CONFLICT);
1403 popup.SetDefaultItem(ID_EDITCONFLICT, FALSE);
1404 popup.AppendMenuIcon(ID_CONFLICTRESOLVE, IDS_SVNPROGRESS_MENUMARKASRESOLVED,IDI_RESOLVE);
1406 popup.AppendMenuIcon(ID_CONFLICTUSETHEIRS, IDS_SVNPROGRESS_MENUUSETHEIRS,IDI_RESOLVE);
1407 popup.AppendMenuIcon(ID_CONFLICTUSEMINE, IDS_SVNPROGRESS_MENUUSEMINE,IDI_RESOLVE);
1409 else if ((data->content_state == svn_wc_notify_state_merged)||(GitProgress_Merge == m_Command)||(data->action == svn_wc_notify_resolved))
1410 popup.SetDefaultItem(ID_COMPARE, FALSE);
1412 if (GetSelectedCount() == 1)
1414 if ((data->action == git_wc_notify_add)||
1415 (data->action == git_wc_notify_revert)||
1416 (data->action == git_wc_notify_resolved)||
1417 (data->action == git_wc_notify_checkout)||
1418 (data->action == git_wc_notify_update_ref))
1420 popup.AppendMenuIcon(ID_LOG, IDS_MENULOG,IDI_LOG);
1421 popup.AppendMenu(MF_SEPARATOR, NULL);
1422 popup.AppendMenuIcon(ID_OPEN, IDS_LOG_POPUP_OPEN, IDI_OPEN);
1423 popup.AppendMenuIcon(ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
1424 bAdded = true;
1427 } // if ((data)&&(!data->path.IsDirectory()))
1428 if (GetSelectedCount() == 1)
1430 if (data)
1432 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1433 CTGitPath path = CTGitPath(sPath);
1434 if (!sPath.IsEmpty())
1436 if (path.GetDirectory().Exists())
1438 popup.AppendMenuIcon(ID_EXPLORE, IDS_SVNPROGRESS_MENUOPENPARENT, IDI_EXPLORER);
1439 bAdded = true;
1444 if (GetSelectedCount() > 0)
1446 if (bAdded)
1447 popup.AppendMenu(MF_SEPARATOR, NULL);
1448 popup.AppendMenuIcon(ID_COPY, IDS_LOG_POPUP_COPYTOCLIPBOARD,IDI_COPYCLIP);
1449 bAdded = true;
1451 if (bAdded)
1453 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1454 #if 0//need
1455 DialogEnableWindow(IDOK, FALSE);
1456 #endif
1457 //this->SetPromptApp(&theApp);
1458 theApp.DoWaitCursor(1);
1459 bool bOpenWith = false;
1460 switch (cmd)
1462 case ID_COPY:
1464 CString sLines;
1465 POSITION pos = GetFirstSelectedItemPosition();
1466 while (pos)
1468 int nItem = GetNextSelectedItem(pos);
1469 NotificationData * data = m_arData[nItem];
1470 if (data)
1472 sLines += data->sPathColumnText;
1473 sLines += _T("\r\n");
1476 sLines.TrimRight();
1477 if (!sLines.IsEmpty())
1479 CStringUtils::WriteAsciiStringToClipboard(sLines, GetSafeHwnd());
1482 break;
1483 case ID_EXPLORE:
1485 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1487 CTGitPath path = CTGitPath(sPath);
1488 ShellExecute(m_hWnd, _T("explore"), path.GetDirectory().GetWinPath(), NULL, path.GetDirectory().GetWinPath(), SW_SHOW);
1490 break;
1491 #if 0
1492 case ID_COMPARE:
1494 svn_revnum_t rev = -1;
1495 StringRevMap::iterator it = m_UpdateStartRevMap.end();
1496 if (data->basepath.IsEmpty())
1497 it = m_UpdateStartRevMap.begin();
1498 else
1499 it = m_UpdateStartRevMap.find(data->basepath.GetSVNApiPath(pool));
1500 if (it != m_UpdateStartRevMap.end())
1501 rev = it->second;
1502 // if the file was merged during update, do a three way diff between OLD, MINE, THEIRS
1503 if (data->content_state == svn_wc_notify_state_merged)
1505 CTGitPath basefile = CTempFiles::Instance().GetTempFilePath(false, data->path, rev);
1506 CTGitPath newfile = CTempFiles::Instance().GetTempFilePath(false, data->path, SVNRev::REV_HEAD);
1507 SVN svn;
1508 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_WC), rev, basefile))
1510 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1511 DialogEnableWindow(IDOK, TRUE);
1512 break;
1514 // If necessary, convert the line-endings on the file before diffing
1515 if ((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\ConvertBase"), TRUE))
1517 CTGitPath temporaryFile = CTempFiles::Instance().GetTempFilePath(false, data->path, SVNRev::REV_BASE);
1518 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_BASE), SVNRev(SVNRev::REV_BASE), temporaryFile))
1520 temporaryFile.Reset();
1521 break;
1523 else
1525 newfile = temporaryFile;
1529 SetFileAttributes(newfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1530 SetFileAttributes(basefile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1531 CString revname, wcname, basename;
1532 revname.Format(_T("%s Revision %ld"), (LPCTSTR)data->path.GetUIFileOrDirectoryName(), rev);
1533 wcname.Format(IDS_DIFF_WCNAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1534 basename.Format(IDS_DIFF_BASENAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1535 CAppUtils::StartExtMerge(basefile, newfile, data->path, data->path, basename, revname, wcname, CString(), true);
1537 else
1539 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, data->path, rev);
1540 SVN svn;
1541 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_WC), rev, tempfile))
1543 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1544 DialogEnableWindow(IDOK, TRUE);
1545 break;
1547 else
1549 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1550 CString revname, wcname;
1551 revname.Format(_T("%s Revision %ld"), (LPCTSTR)data->path.GetUIFileOrDirectoryName(), rev);
1552 wcname.Format(IDS_DIFF_WCNAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1553 CAppUtils::StartExtDiff(
1554 tempfile, data->path, revname, wcname,
1555 CAppUtils::DiffFlags().AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000)));
1559 break;
1560 case ID_EDITCONFLICT:
1562 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1563 SVNDiff::StartConflictEditor(CTGitPath(sPath));
1565 break;
1566 case ID_CONFLICTUSETHEIRS:
1567 case ID_CONFLICTUSEMINE:
1568 case ID_CONFLICTRESOLVE:
1570 svn_wc_conflict_choice_t result = svn_wc_conflict_choose_merged;
1571 switch (cmd)
1573 case ID_CONFLICTUSETHEIRS:
1574 result = svn_wc_conflict_choose_theirs_full;
1575 break;
1576 case ID_CONFLICTUSEMINE:
1577 result = svn_wc_conflict_choose_mine_full;
1578 break;
1579 case ID_CONFLICTRESOLVE:
1580 result = svn_wc_conflict_choose_merged;
1581 break;
1583 SVN svn;
1584 POSITION pos = GetFirstSelectedItemPosition();
1585 CString sResolvedPaths;
1586 while (pos)
1588 int nItem = GetNextSelectedItem(pos);
1589 NotificationData * data = m_arData[nItem];
1590 if (data)
1592 if (data->bConflictedActionItem)
1594 if (!svn.Resolve(data->path, result, FALSE))
1596 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1597 DialogEnableWindow(IDOK, TRUE);
1598 break;
1600 else
1602 data->color = ::GetSysColor(COLOR_WINDOWTEXT);
1603 data->action = svn_wc_notify_resolved;
1604 data->sActionColumnText.LoadString(IDS_SVNACTION_RESOLVE);
1605 data->bConflictedActionItem = false;
1606 m_nConflicts--;
1608 if (m_nConflicts==0)
1610 // When the last conflict is resolved we remove
1611 // the warning which we assume is in the last line.
1612 int nIndex = GetItemCount()-1;
1613 VERIFY(DeleteItem(nIndex));
1615 delete m_arData[nIndex];
1616 m_arData.pop_back();
1618 sResolvedPaths += data->path.GetWinPathString() + _T("\n");
1623 Invalidate();
1624 CString info = BuildInfoString();
1625 SetDlgItemText(IDC_INFOTEXT, info);
1627 if (!sResolvedPaths.IsEmpty())
1629 CString msg;
1630 msg.Format(IDS_SVNPROGRESS_RESOLVED, (LPCTSTR)sResolvedPaths);
1631 CMessageBox::Show(m_hWnd, msg, _T("TortoiseGit"), MB_OK | MB_ICONINFORMATION);
1634 break;
1635 #endif
1636 case ID_LOG:
1638 CString cmd = _T("/command:log");
1639 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1640 if(data->action == git_wc_notify_update_ref)
1642 cmd += _T(" /path:\"") + GetPathFromColumnText(CString()) + _T("\"");
1643 if (!data->m_OldHash.IsEmpty())
1644 cmd += _T(" /startrev:") + data->m_OldHash.ToString();
1645 if (!data->m_NewHash.IsEmpty())
1646 cmd += _T(" /endrev:") + data->m_NewHash.ToString();
1648 else
1649 cmd += _T(" /path:\"") + sPath + _T("\"");
1650 CAppUtils::RunTortoiseGitProc(cmd);
1652 break;
1653 case ID_OPENWITH:
1654 bOpenWith = true;
1655 case ID_OPEN:
1657 int ret = 0;
1658 CString sWinPath = GetPathFromColumnText(data->sPathColumnText);
1659 if (!bOpenWith)
1660 ret = (int)ShellExecute(this->m_hWnd, NULL, (LPCTSTR)sWinPath, NULL, NULL, SW_SHOWNORMAL);
1661 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
1663 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1664 cmd += sWinPath + _T(" ");
1665 CAppUtils::LaunchApplication(cmd, NULL, false);
1669 #if 0 //need
1670 DialogEnableWindow(IDOK, TRUE);
1671 #endif
1672 theApp.DoWaitCursor(-1);
1673 } // if (bAdded)
1679 void CGitProgressList::OnLvnBegindragSvnprogress(NMHDR* , LRESULT *pResult)
1681 //LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1682 #if 0
1683 int selIndex = GetSelectionMark();
1684 if (selIndex < 0)
1685 return;
1687 CDropFiles dropFiles; // class for creating DROPFILES struct
1689 int index;
1690 POSITION pos = GetFirstSelectedItemPosition();
1691 while ( (index = GetNextSelectedItem(pos)) >= 0 )
1693 NotificationData * data = m_arData[index];
1695 if ( data->kind==svn_node_file || data->kind==svn_node_dir )
1697 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1699 dropFiles.AddFile( sPath );
1703 if ( dropFiles.GetCount()>0 )
1705 dropFiles.CreateStructure();
1707 #endif
1708 *pResult = 0;
1711 void CGitProgressList::OnSize(UINT nType, int cx, int cy)
1713 CListCtrl::OnSize(nType, cx, cy);
1714 if ((nType == SIZE_RESTORED)&&(m_bLastVisible))
1716 if(!m_hWnd)
1717 return;
1719 int count = GetItemCount();
1720 if (count > 0)
1721 EnsureVisible(count-1, false);
1725 //////////////////////////////////////////////////////////////////////////
1726 /// commands
1727 //////////////////////////////////////////////////////////////////////////
1728 bool CGitProgressList::CmdAdd(CString& sWindowTitle, bool& localoperation)
1730 localoperation = true;
1731 SetWindowTitle(IDS_PROGRS_TITLE_ADD, m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
1732 SetBackgroundImage(IDI_ADD_BKG);
1733 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_ADD)));
1735 if (CRegDWORD(_T("Software\\TortoiseGit\\UseLibgit2"), TRUE) == TRUE)
1737 git_repository *repo = NULL;
1738 git_index *index;
1740 CStringA gitdir = CUnicodeUtils::GetMulti(CTGitPath(g_Git.m_CurrentDir).GetGitPathString(), CP_UTF8);
1741 if (git_repository_open(&repo, gitdir.GetBuffer()))
1743 gitdir.ReleaseBuffer();
1744 ReportGitError();
1745 return false;
1747 gitdir.ReleaseBuffer();
1749 git_config * config;
1750 git_config_new(&config);
1752 CStringA projectConfigA = CUnicodeUtils::GetMulti(g_Git.GetGitLocalConfig(), CP_UTF8);
1753 if (git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 4, FALSE))
1755 projectConfigA.ReleaseBuffer();
1756 ReportGitError();
1757 git_config_free(config);
1758 git_repository_free(repo);
1759 return false;
1761 projectConfigA.ReleaseBuffer();
1762 CString globalConfig = g_Git.GetGitGlobalConfig();
1763 if (PathFileExists(globalConfig))
1765 CStringA globalConfigA = CUnicodeUtils::GetMulti(globalConfig, CP_UTF8);
1766 if (git_config_add_file_ondisk(config, globalConfigA.GetBuffer(), 3, FALSE))
1768 globalConfigA.ReleaseBuffer();
1769 ReportGitError();
1770 git_config_free(config);
1771 git_repository_free(repo);
1772 return false;
1774 globalConfigA.ReleaseBuffer();
1776 CString globalXDGConfig = g_Git.GetGitGlobalXDGConfig();
1777 if (PathFileExists(globalXDGConfig))
1779 CStringA globalXDGConfigA = CUnicodeUtils::GetMulti(globalXDGConfig, CP_UTF8);
1780 if (git_config_add_file_ondisk(config, globalXDGConfigA.GetBuffer(), 2, FALSE))
1782 globalXDGConfigA.ReleaseBuffer();
1783 ReportGitError();
1784 git_config_free(config);
1785 git_repository_free(repo);
1786 return false;
1788 globalXDGConfigA.ReleaseBuffer();
1790 CString systemConfig = g_Git.GetGitSystemConfig();
1791 if (!systemConfig.IsEmpty())
1793 CStringA systemConfigA = CUnicodeUtils::GetMulti(systemConfig, CP_UTF8);
1794 if (git_config_add_file_ondisk(config, systemConfigA.GetBuffer(), 1, FALSE))
1796 systemConfigA.ReleaseBuffer();
1797 ReportGitError();
1798 git_config_free(config);
1799 git_repository_free(repo);
1800 return false;
1802 systemConfigA.ReleaseBuffer();
1805 git_repository_set_config(repo, config);
1806 git_config_free(config);
1807 config = nullptr;
1809 if (git_repository_index(&index, repo))
1811 ReportGitError();
1812 git_repository_free(repo);
1813 return false;
1815 if (git_index_read(index))
1817 ReportGitError();
1818 git_index_free(index);
1819 git_repository_free(repo);
1820 return false;
1823 for (int i = 0; i < m_targetPathList.GetCount(); ++i)
1825 if (git_index_add_bypath(index, CStringA(CUnicodeUtils::GetMulti(m_targetPathList[i].GetGitPathString(), CP_UTF8)).GetBuffer()))
1827 ReportGitError();
1828 git_index_free(index);
1829 git_repository_free(repo);
1830 return false;
1832 Notify(m_targetPathList[i],git_wc_notify_add);
1835 if (git_index_write(index))
1837 ReportGitError();
1838 git_index_free(index);
1839 git_repository_free(repo);
1840 return false;
1843 git_index_free(index);
1844 git_repository_free(repo);
1846 else
1848 CMassiveGitTask mgt(L"add -f");
1849 mgt.ExecuteWithNotify(&m_targetPathList, m_bCancelled, git_wc_notify_add, this, &CGitProgressList::Notify);
1852 CShellUpdater::Instance().AddPathsForUpdate(m_targetPathList);
1854 return true;
1857 bool CGitProgressList::CmdCopy(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1859 #if 0
1860 ASSERT(m_targetPathList.GetCount() == 1);
1861 sWindowTitle.LoadString(IDS_PROGRS_TITLE_COPY);
1862 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1863 SetBackgroundImage(IDI_COPY_BKG);
1865 CString sCmdInfo;
1866 sCmdInfo.Format(IDS_PROGRS_CMD_COPY,
1867 m_targetPathList[0].IsUrl() ? (LPCTSTR)m_targetPathList[0].GetSVNPathString() : m_targetPathList[0].GetWinPath(),
1868 (LPCTSTR)m_url.GetSVNPathString(), (LPCTSTR)m_Revision.ToString());
1869 ReportCmd(sCmdInfo);
1871 if (!Copy(m_targetPathList, m_url, m_Revision, m_pegRev, m_sMessage))
1873 ReportSVNError();
1874 return false;
1876 if (m_options & ProgOptSwitchAfterCopy)
1878 sCmdInfo.Format(IDS_PROGRS_CMD_SWITCH,
1879 m_targetPathList[0].GetWinPath(),
1880 (LPCTSTR)m_url.GetSVNPathString(), (LPCTSTR)m_Revision.ToString());
1881 ReportCmd(sCmdInfo);
1882 if (!Switch(m_targetPathList[0], m_url, SVNRev::REV_HEAD, SVNRev::REV_HEAD, m_depth, TRUE, m_options & ProgOptIgnoreExternals))
1884 if (!Switch(m_targetPathList[0], m_url, SVNRev::REV_HEAD, m_Revision, m_depth, TRUE, m_options & ProgOptIgnoreExternals))
1886 ReportSVNError();
1887 return false;
1891 else
1893 if (SVN::PathIsURL(m_url))
1895 CString sMsg(MAKEINTRESOURCE(IDS_PROGRS_COPY_WARNING));
1896 ReportNotification(sMsg);
1899 #endif
1900 return true;
1903 bool CGitProgressList::CmdExport(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1905 #if 0
1906 ASSERT(m_targetPathList.GetCount() == 1);
1907 sWindowTitle.LoadString(IDS_PROGRS_TITLE_EXPORT);
1908 sWindowTitle = m_url.GetUIFileOrDirectoryName()+_T(" - ")+sWindowTitle;
1909 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1910 SetBackgroundImage(IDI_EXPORT_BKG);
1911 CString eol;
1912 if (m_options & ProgOptEolCRLF)
1913 eol = _T("CRLF");
1914 if (m_options & ProgOptEolLF)
1915 eol = _T("LF");
1916 if (m_options & ProgOptEolCR)
1917 eol = _T("CR");
1918 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_EXPORT)));
1919 if (!Export(m_url, m_targetPathList[0], m_Revision, m_Revision, TRUE, m_options & ProgOptIgnoreExternals, m_depth, NULL, FALSE, eol))
1921 ReportSVNError();
1922 return false;
1924 #endif
1925 return true;
1928 bool CGitProgressList::CmdRename(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1930 #if 0
1931 ASSERT(m_targetPathList.GetCount() == 1);
1932 if ((!m_targetPathList[0].IsUrl())&&(!m_url.IsUrl()))
1933 localoperation = true;
1934 sWindowTitle.LoadString(IDS_PROGRS_TITLE_RENAME);
1935 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1936 SetBackgroundImage(IDI_RENAME_BKG);
1937 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RENAME)));
1938 if (!Move(m_targetPathList, m_url, m_Revision, m_sMessage))
1940 ReportSVNError();
1941 return false;
1943 #endif
1944 return true;
1947 bool CGitProgressList::CmdResolve(CString& sWindowTitle, bool& localoperation)
1950 localoperation = true;
1951 ASSERT(m_targetPathList.GetCount() == 1);
1952 SetWindowTitle(IDS_PROGRS_TITLE_RESOLVE, m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
1953 SetBackgroundImage(IDI_RESOLVE_BKG);
1954 // check if the file may still have conflict markers in it.
1955 //BOOL bMarkers = FALSE;
1957 for (int i = 0; i < m_targetPathList.GetCount(); ++i)
1959 CString cmd,out,tempmergefile;
1960 cmd.Format(_T("git.exe add -f -- \"%s\""),m_targetPathList[i].GetGitPathString());
1961 if (g_Git.Run(cmd, &out, CP_UTF8))
1963 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1964 m_bErrorsOccurred=true;
1965 return false;
1968 CAppUtils::RemoveTempMergeFile((CTGitPath &)m_targetPathList[i]);
1970 Notify(m_targetPathList[i],git_wc_notify_resolved);
1972 #if 0
1973 if ((m_options & ProgOptSkipConflictCheck) == 0)
1977 for (INT_PTR fileindex=0; (fileindex<m_targetPathList.GetCount()) && (bMarkers==FALSE); ++fileindex)
1979 if (!m_targetPathList[fileindex].IsDirectory())
1981 CStdioFile file(m_targetPathList[fileindex].GetWinPath(), CFile::typeBinary | CFile::modeRead);
1982 CString strLine = _T("");
1983 while (file.ReadString(strLine))
1985 if (strLine.Find(_T("<<<<<<<"))==0)
1987 bMarkers = TRUE;
1988 break;
1991 file.Close();
1995 catch (CFileException* pE)
1997 TRACE(_T("CFileException in Resolve!\n"));
1998 TCHAR error[10000] = {0};
1999 pE->GetErrorMessage(error, 10000);
2000 ReportError(error);
2001 pE->Delete();
2002 return false;
2005 if (bMarkers)
2007 if (CMessageBox::Show(m_hWnd, IDS_PROGRS_REVERTMARKERS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION)==IDYES)
2009 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RESOLVE)));
2010 for (INT_PTR fileindex=0; fileindex<m_targetPathList.GetCount(); ++fileindex)
2011 Resolve(m_targetPathList[fileindex], svn_wc_conflict_choose_merged, true);
2014 else
2016 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RESOLVE)));
2017 for (INT_PTR fileindex=0; fileindex<m_targetPathList.GetCount(); ++fileindex)
2018 Resolve(m_targetPathList[fileindex], svn_wc_conflict_choose_merged, true);
2020 #endif
2021 CShellUpdater::Instance().AddPathsForUpdate(m_targetPathList);
2023 return true;
2026 bool CGitProgressList::CmdRevert(CString& sWindowTitle, bool& localoperation)
2029 localoperation = true;
2030 SetWindowTitle(IDS_PROGRS_TITLE_REVERT, m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
2031 SetBackgroundImage(IDI_REVERT_BKG);
2033 CTGitPathList delList;
2034 for (int i = 0; i < m_selectedPaths.GetCount(); ++i)
2036 CTGitPath path;
2037 int action;
2038 path.SetFromWin(g_Git.m_CurrentDir+_T("\\")+m_selectedPaths[i].GetWinPath());
2039 action = m_selectedPaths[i].m_Action;
2040 /* rename file can't delete because it needs original file*/
2041 if((!(action & CTGitPath::LOGACTIONS_ADDED)) &&
2042 (!(action & CTGitPath::LOGACTIONS_REPLACED)))
2043 delList.AddPath(path);
2045 if (DWORD(CRegDWORD(_T("Software\\TortoiseGit\\RevertWithRecycleBin"), TRUE)))
2046 delList.DeleteAllFiles(true);
2048 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_REVERT)));
2049 for (int i = 0; i < m_selectedPaths.GetCount(); ++i)
2051 if(g_Git.Revert(_T("HEAD"), (CTGitPath&)m_selectedPaths[i]))
2053 CMessageBox::Show(NULL,_T("Revert Fail"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
2054 m_bErrorsOccurred=true;
2055 return false;
2057 Notify(m_selectedPaths[i],git_wc_notify_revert);
2060 CShellUpdater::Instance().AddPathsForUpdate(m_selectedPaths);
2062 return true;
2065 bool CGitProgressList::CmdSwitch(CString& /*sWindowTitle*/, bool& /*localoperation*/)
2067 #if 0
2068 ASSERT(m_targetPathList.GetCount() == 1);
2069 SVNStatus st;
2070 sWindowTitle.LoadString(IDS_PROGRS_TITLE_SWITCH);
2071 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
2072 SetBackgroundImage(IDI_SWITCH_BKG);
2073 LONG rev = 0;
2074 if (st.GetStatus(m_targetPathList[0]) != (-2))
2076 if (st.status->entry != NULL)
2078 rev = st.status->entry->revision;
2082 CString sCmdInfo;
2083 sCmdInfo.Format(IDS_PROGRS_CMD_SWITCH,
2084 m_targetPathList[0].GetWinPath(), (LPCTSTR)m_url.GetSVNPathString(),
2085 (LPCTSTR)m_Revision.ToString());
2086 ReportCmd(sCmdInfo);
2088 bool depthIsSticky = true;
2089 if (m_depth == svn_depth_unknown)
2090 depthIsSticky = false;
2091 if (!Switch(m_targetPathList[0], m_url, m_Revision, m_Revision, m_depth, depthIsSticky, m_options & ProgOptIgnoreExternals))
2093 ReportSVNError();
2094 return false;
2096 m_UpdateStartRevMap[m_targetPathList[0].GetSVNApiPath(pool)] = rev;
2097 if ((m_RevisionEnd >= 0)&&(rev >= 0)
2098 &&((LONG)m_RevisionEnd > (LONG)rev))
2100 GetDlgItem(IDC_LOGBUTTON)->ShowWindow(SW_SHOW);
2102 #endif
2103 return true;
2105 bool CGitProgressList::CmdClone(CString& sWindowTitle, bool& /*localoperation*/)
2107 if (!g_Git.UsingLibGit2(CGit::GIT_CMD_CLONE))
2109 // should never run to here
2110 ASSERT(FALSE);
2111 return false;
2113 this->m_TotalBytesTransferred = 0;
2115 SetWindowTitle(IDS_PROGRS_TITLE_CLONE, m_url.GetGitPathString(), sWindowTitle);
2116 SetBackgroundImage(IDI_SWITCH_BKG);
2117 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROG_CLONE)));
2119 if (m_url.IsEmpty() || m_targetPathList.GetCount() == 0)
2120 return false;
2122 CStringA url = CUnicodeUtils::GetMulti(m_url.GetGitPathString(), CP_UTF8);
2123 CStringA path = CUnicodeUtils::GetMulti(m_targetPathList[0].GetWinPathString(),CP_UTF8);
2125 git_repository *cloned_repo = NULL;
2126 git_remote *origin = NULL;
2127 git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
2129 int error = 0;
2131 git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
2133 clone_opts.checkout_opts = checkout_opts;
2135 clone_opts.checkout_opts.checkout_strategy = m_bNoCheckout? GIT_CHECKOUT_NONE : GIT_CHECKOUT_SAFE_CREATE;
2136 clone_opts.checkout_opts.progress_cb = CheckoutCallback;
2137 clone_opts.checkout_opts.progress_payload = this;
2139 git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
2141 callbacks.update_tips = RemoteUpdatetipsCallback;
2142 callbacks.progress = RemoteProgressCallback;
2143 callbacks.completion = RemoteCompletionCallback;
2144 callbacks.payload = this;
2146 clone_opts.remote_callbacks = &callbacks;
2148 if (!m_RefSpec.IsEmpty())
2149 clone_opts.checkout_branch = CUnicodeUtils::GetMulti(m_RefSpec, CP_UTF8).GetBuffer();
2151 clone_opts.fetch_progress_cb = FetchCallback;
2152 clone_opts.fetch_progress_payload = this;
2154 clone_opts.cred_acquire_cb = CAppUtils::Git2GetUserPassword;
2156 clone_opts.bare = m_bBare;
2158 if(m_pAnimate)
2160 m_pAnimate->ShowWindow(SW_SHOW);
2161 m_pAnimate->Play(0, INT_MAX, INT_MAX);
2163 error = git_clone(&cloned_repo, url, path, &clone_opts);
2165 if (m_pAnimate)
2167 m_pAnimate->Stop();
2168 m_pAnimate->ShowWindow(SW_HIDE);
2171 git_remote_free(origin);
2172 if (error)
2174 ReportGitError();
2175 return false;
2177 else if (cloned_repo)
2178 git_repository_free(cloned_repo);
2179 return true;
2181 bool CGitProgressList::CmdSendMail(CString& sWindowTitle, bool& /*localoperation*/)
2183 SetWindowTitle(IDS_PROGRS_TITLE_SENDMAIL, m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
2184 //SetBackgroundImage(IDI_ADD_BKG);
2185 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_SENDMAIL)));
2186 bool ret=true;
2187 if(this->m_SendMailFlags&SENDMAIL_COMBINED)
2189 CString error;
2190 CTGitPath path;
2191 Notify(path,git_wc_notify_sendmail_start);
2192 CString err;
2193 int retry=0;
2194 while(retry <3)
2196 if(!!CPatch::SendPatchesCombined(m_targetPathList,m_SendMailTO,m_SendMailCC,m_SendMailSubject,!!(this->m_SendMailFlags&SENDMAIL_ATTACHMENT),!!(this->m_SendMailFlags&SENDMAIL_MAPI),&err))
2198 Notify(path,git_wc_notify_sendmail_error,ret,&err);
2199 ret = false;
2201 else
2203 break;
2206 ++retry;
2207 if (retry < 3)
2208 Notify(path,git_wc_notify_sendmail_retry,ret,&err);
2209 Sleep(2000);
2210 if(m_bCancelled)
2212 CString str;
2213 str.LoadString(IDS_SVN_USERCANCELLED);
2214 Notify(path,git_wc_notify_sendmail_error,ret,&str);
2215 return false;
2218 if (ret)
2219 Notify(path,git_wc_notify_sendmail_done,ret);
2221 else
2223 for (int i = 0; ret && i < m_targetPathList.GetCount(); ++i)
2225 CPatch patch;
2226 Notify(m_targetPathList[i],git_wc_notify_sendmail_start);
2228 int retry=0;
2229 while(retry<3)
2231 if(!!patch.Send((CString&)m_targetPathList[i].GetWinPathString(),this->m_SendMailTO,
2232 this->m_SendMailCC,!!(this->m_SendMailFlags&SENDMAIL_ATTACHMENT),!!(this->m_SendMailFlags&SENDMAIL_MAPI)))
2234 Notify(m_targetPathList[i],git_wc_notify_sendmail_error,ret,&patch.m_LastError);
2235 ret = false;
2238 else
2240 ret = true;
2241 break;
2243 ++retry;
2244 if (retry < 3)
2245 Notify(m_targetPathList[i],git_wc_notify_sendmail_retry,ret,&patch.m_LastError);
2246 Sleep(2000);
2247 if(m_bCancelled)
2249 CString str;
2250 str.LoadString(IDS_SVN_USERCANCELLED);
2251 Notify(m_targetPathList[i],git_wc_notify_sendmail_error,ret,&str);
2252 return false;
2255 if (ret)
2256 Notify(m_targetPathList[i],git_wc_notify_sendmail_done,ret);
2259 return ret;
2262 bool CGitProgressList::CmdFetch(CString& sWindowTitle, bool& /*localoperation*/)
2264 if (!g_Git.UsingLibGit2(CGit::GIT_CMD_CLONE))
2266 // should never run to here
2267 ASSERT(0);
2268 return false;
2270 this->m_TotalBytesTransferred = 0;
2272 SetWindowTitle(IDS_PROGRS_TITLE_FETCH, g_Git.m_CurrentDir, sWindowTitle);
2273 SetBackgroundImage(IDI_UPDATE_BKG);
2274 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_TITLE_FETCH)) + _T(" ") + m_url.GetGitPathString() + _T(" ") + m_RefSpec);
2276 CStringA url = CUnicodeUtils::GetMulti(m_url.GetGitPathString(), CP_UTF8);
2277 CStringA gitdir = CUnicodeUtils::GetMulti(CTGitPath(g_Git.m_CurrentDir).GetGitPathString(), CP_UTF8);
2278 CStringA remotebranch = CUnicodeUtils::GetMulti(m_RefSpec, CP_UTF8);
2280 git_remote *remote = NULL;
2281 git_repository *repo = NULL;
2282 bool ret = true;
2286 if (m_pAnimate)
2288 m_pAnimate->ShowWindow(SW_SHOW);
2289 m_pAnimate->Play(0, INT_MAX, INT_MAX);
2292 if (git_repository_open(&repo, gitdir.GetBuffer()))
2294 gitdir.ReleaseBuffer();
2295 ReportGitError();
2296 ret = false;
2297 break;
2300 // first try with a named remote (e.g. "origin")
2301 if (git_remote_load(&remote, repo, url) < 0)
2303 // retry with repository located at a specific url
2304 if (git_remote_create_inmemory(&remote, repo, NULL, url) < 0)
2306 ReportGitError();
2307 ret = false;
2308 break;
2312 git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
2314 callbacks.update_tips = RemoteUpdatetipsCallback;
2315 callbacks.progress = RemoteProgressCallback;
2316 callbacks.completion = RemoteCompletionCallback;
2317 callbacks.payload = this;
2319 git_remote_set_callbacks(remote, &callbacks);
2320 git_remote_set_cred_acquire_cb(remote, CAppUtils::Git2GetUserPassword, NULL);
2321 git_remote_set_autotag(remote, (git_remote_autotag_option_t)m_AutoTag);
2323 if (!remotebranch.IsEmpty() && git_remote_set_fetchspec(remote, remotebranch))
2325 ReportGitError();
2326 ret = false;
2327 break;
2330 // Connect to the remote end specifying that we want to fetch
2331 // information from it.
2332 if (git_remote_connect(remote, GIT_DIRECTION_FETCH) < 0) {
2333 ReportGitError();
2334 ret = false;
2335 break;
2338 // Download the packfile and index it. This function updates the
2339 // amount of received data and the indexer stats which lets you
2340 // inform the user about progress.
2341 if (git_remote_download(remote, FetchCallback, this) < 0) {
2342 ReportGitError();
2343 ret = false;
2344 break;
2347 if (m_pAnimate)
2349 m_pAnimate->ShowWindow(SW_HIDE);
2351 // Update the references in the remote's namespace to point to the
2352 // right commits. This may be needed even if there was no packfile
2353 // to download, which can happen e.g. when the branches have been
2354 // changed but all the neede objects are available locally.
2355 if (git_remote_update_tips(remote) < 0)
2357 ReportGitError();
2358 ret = false;
2359 break;
2362 git_remote_disconnect(remote);
2364 } while(0);
2366 git_remote_free(remote);
2367 git_repository_free(repo);
2368 if (m_pAnimate)
2369 m_pAnimate->ShowWindow(SW_HIDE);
2370 return ret;
2374 void CGitProgressList::Init()
2376 SetExtendedStyle (LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
2378 DeleteAllItems();
2379 int c = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
2380 while (c>=0)
2381 DeleteColumn(c--);
2383 CString temp;
2384 temp.LoadString(IDS_PROGRS_ACTION);
2385 InsertColumn(0, temp);
2386 temp.LoadString(IDS_PROGRS_PATH);
2387 InsertColumn(1, temp);
2389 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
2390 if (m_pThread==NULL)
2392 ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
2394 else
2396 m_pThread->m_bAutoDelete = FALSE;
2397 m_pThread->ResumeThread();
2400 // Call this early so that the column headings aren't hidden before any
2401 // text gets added.
2402 ResizeColumns();
2404 SetTimer(VISIBLETIMER, 300, NULL);
2408 void CGitProgressList::OnClose()
2410 // TODO: Add your message handler code here and/or call default
2411 if (m_bCancelled)
2413 TerminateThread(m_pThread->m_hThread, (DWORD)-1);
2414 InterlockedExchange(&m_bThreadRunning, FALSE);
2416 else
2418 m_bCancelled = TRUE;
2419 return;
2421 CListCtrl::OnClose();
2425 BOOL CGitProgressList::PreTranslateMessage(MSG* pMsg)
2427 // TODO: Add your specialized code here and/or call the base class
2428 if (pMsg->message == WM_KEYDOWN)
2430 if (pMsg->wParam == 'A')
2432 if (GetKeyState(VK_CONTROL)&0x8000)
2434 // Ctrl-A -> select all
2435 SetSelectionMark(0);
2436 for (int i=0; i<GetItemCount(); ++i)
2438 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2442 if ((pMsg->wParam == 'C')||(pMsg->wParam == VK_INSERT))
2444 int selIndex = GetSelectionMark();
2445 if (selIndex >= 0)
2447 if (GetKeyState(VK_CONTROL)&0x8000)
2449 //Ctrl-C -> copy to clipboard
2450 CString sClipdata;
2451 POSITION pos = GetFirstSelectedItemPosition();
2452 if (pos != NULL)
2454 while (pos)
2456 int nItem = GetNextSelectedItem(pos);
2457 CString sAction = GetItemText(nItem, 0);
2458 CString sPath = GetItemText(nItem, 1);
2459 CString sMime = GetItemText(nItem, 2);
2460 CString sLogCopyText;
2461 sLogCopyText.Format(_T("%s: %s %s\r\n"),
2462 (LPCTSTR)sAction, (LPCTSTR)sPath, (LPCTSTR)sMime);
2463 sClipdata += sLogCopyText;
2465 CStringUtils::WriteAsciiStringToClipboard(sClipdata);
2470 } // if (pMsg->message == WM_KEYDOWN)
2471 return CListCtrl::PreTranslateMessage(pMsg);
2474 CString CGitProgressList::GetPathFromColumnText(const CString& sColumnText)
2476 CString sPath = sColumnText;
2477 if (sPath.Find(':')<0)
2479 // the path is not absolute: add the common root of all paths to it
2480 sPath = g_Git.m_CurrentDir + _T("\\") + sColumnText;
2482 return sPath;
2485 void CGitProgressList::SetWindowTitle(UINT id, const CString& urlorpath, CString& dialogname)
2487 if (!m_bSetTitle || !m_pPostWnd)
2488 return;
2490 dialogname.LoadString(id);
2491 CAppUtils::SetWindowTitle(m_pPostWnd->GetSafeHwnd(), urlorpath, dialogname);