Fix typo that prevented from using libgit2 to fetch
[TortoiseGit.git] / src / TortoiseProc / GitProgressList.cpp
blobcd8427082ef16d3250e1e4412c1a880d80f42762
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 BOOL CGitProgressList::m_bAscending = FALSE;
51 int CGitProgressList::m_nSortedColumn = -1;
53 #define TRANSFERTIMER 100
54 #define VISIBLETIMER 101
55 // CGitProgressList
57 enum GITProgressDlgContextMenuCommands
59 // needs to start with 1, since 0 is the return value if *nothing* is clicked on in the context menu
60 ID_COMPARE = 1,
61 ID_EDITCONFLICT,
62 ID_CONFLICTRESOLVE,
63 ID_CONFLICTUSETHEIRS,
64 ID_CONFLICTUSEMINE,
65 ID_LOG,
66 ID_OPEN,
67 ID_OPENWITH,
68 ID_EXPLORE,
69 ID_COPY
72 IMPLEMENT_DYNAMIC(CGitProgressList, CListCtrl)
74 CGitProgressList::CGitProgressList():CListCtrl()
75 , m_bCancelled(FALSE)
76 , m_pThread(NULL)
77 , m_bErrorsOccurred(false)
78 , m_bBare(false)
79 , m_bNoCheckout(false)
80 , m_AutoTag(GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
81 , m_options(ProgOptNone)
82 , m_bSetTitle(false)
83 , m_pTaskbarList(nullptr)
84 , m_SendMail(nullptr)
86 m_pInfoCtrl = nullptr;
87 m_pAnimate = nullptr;
88 m_pProgControl = nullptr;
89 m_pProgressLabelCtrl = nullptr;
90 m_pPostWnd = nullptr;
93 CGitProgressList::~CGitProgressList()
95 for (size_t i = 0; i < m_arData.size(); ++i)
97 delete m_arData[i];
99 if(m_pThread != NULL)
101 delete m_pThread;
106 BEGIN_MESSAGE_MAP(CGitProgressList, CListCtrl)
107 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdrawSvnprogress)
108 ON_NOTIFY_REFLECT(NM_DBLCLK, OnNMDblclkSvnprogress)
109 ON_NOTIFY_REFLECT(HDN_ITEMCLICK, OnHdnItemclickSvnprogress)
110 ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnLvnBegindragSvnprogress)
111 ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetdispinfoSvnprogress)
112 ON_MESSAGE(WM_SHOWCONFLICTRESOLVER, OnShowConflictResolver)
113 ON_WM_SIZE()
114 ON_WM_TIMER()
115 ON_WM_CONTEXTMENU()
116 ON_WM_CLOSE()
117 END_MESSAGE_MAP()
119 void CGitProgressList::Cancel()
121 m_bCancelled = TRUE;
126 // CGitProgressList message handlers
129 LRESULT CGitProgressList::OnShowConflictResolver(WPARAM /*wParam*/, LPARAM /*lParam*/)
131 #if 0
132 CConflictResolveDlg dlg(this);
133 const svn_wc_conflict_description_t *description = (svn_wc_conflict_description_t *)lParam;
134 if (description)
136 dlg.SetConflictDescription(description);
137 if (m_pTaskbarList)
139 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_PAUSED);
141 if (dlg.DoModal() == IDOK)
143 if (dlg.GetResult() == svn_wc_conflict_choose_postpone)
145 // if the result is conflicted and the dialog returned IDOK,
146 // that means we should not ask again in case of a conflict
147 m_AlwaysConflicted = true;
148 ::SendMessage(GetDlgItem(IDC_NONINTERACTIVE)->GetSafeHwnd(), BM_SETCHECK, BST_CHECKED, 0);
151 m_mergedfile = dlg.GetMergedFile();
152 m_bCancelled = dlg.IsCancelled();
153 if (m_pTaskbarList)
154 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_INDETERMINATE);
155 return dlg.GetResult();
158 return svn_wc_conflict_choose_postpone;
159 #endif
160 return 0;
162 #if 0
163 svn_wc_conflict_choice_t CGitProgressList::ConflictResolveCallback(const svn_wc_conflict_description_t *description, CString& mergedfile)
165 // we only bother the user when merging
166 if (((m_Command == GitProgress_Merge)||(m_Command == GitProgress_MergeAll)||(m_Command == GitProgress_MergeReintegrate))&&(!m_AlwaysConflicted)&&(description))
168 // we're in a worker thread here. That means we must not show a dialog from the thread
169 // but let the UI thread do it.
170 // To do that, we send a message to the UI thread and let it show the conflict resolver dialog.
171 LRESULT dlgResult = ::SendMessage(GetSafeHwnd(), WM_SHOWCONFLICTRESOLVER, 0, (LPARAM)description);
172 mergedfile = m_mergedfile;
173 return (svn_wc_conflict_choice_t)dlgResult;
176 return svn_wc_conflict_choose_postpone;
178 #endif
179 void CGitProgressList::AddItemToList()
181 int totalcount = GetItemCount();
183 SetItemCountEx(totalcount+1, LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
184 // make columns width fit
185 if (iFirstResized < 30)
187 // only resize the columns for the first 30 or so entries.
188 // after that, don't resize them anymore because that's an
189 // expensive function call and the columns will be sized
190 // close enough already.
191 ResizeColumns();
192 ++iFirstResized;
195 // Make sure the item is *entirely* visible even if the horizontal
196 // scroll bar is visible.
197 int count = GetCountPerPage();
198 if (totalcount <= (GetTopIndex() + count + nEnsureVisibleCount + 2))
200 ++nEnsureVisibleCount;
201 m_bLastVisible = true;
203 else
205 nEnsureVisibleCount = 0;
206 if (IsIconic() == 0)
207 m_bLastVisible = false;
212 BOOL CGitProgressList::Notify(const CTGitPath& path, git_wc_notify_action_t action
214 svn_node_kind_t kind, const CString& mime_type,
215 svn_wc_notify_state_t content_state,
216 svn_wc_notify_state_t prop_state, LONG rev,
217 const svn_lock_t * lock, svn_wc_notify_lock_state_t lock_state,
218 const CString& changelistname,
219 svn_merge_range_t * range,
220 svn_error_t * err, apr_pool_t * pool
223 bool bNoNotify = false;
224 bool bDoAddData = true;
225 NotificationData * data = new NotificationData();
226 data->path = path;
227 data->action = action;
228 data->sPathColumnText=path.GetGitPathString();
229 data->bAuxItem = false;
231 if (this->m_pAnimate)
232 this->m_pAnimate->ShowWindow(SW_HIDE);
234 #if 0
235 data->kind = kind;
236 data->mime_type = mime_type;
237 data->content_state = content_state;
238 data->prop_state = prop_state;
239 data->rev = rev;
240 data->lock_state = lock_state;
241 data->changelistname = changelistname;
242 if ((lock)&&(lock->owner))
243 data->owner = CUnicodeUtils::GetUnicode(lock->owner);
244 data->sPathColumnText = path.GetUIPathString();
245 if (!m_basePath.IsEmpty())
246 data->basepath = m_basePath;
247 if (range)
248 data->merge_range = *range;
249 #endif
250 switch (data->action)
252 case git_wc_notify_add:
253 //case svn_wc_notify_update_add:
254 // if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
255 // {
256 // data->color = m_Colors.GetColor(CColors::Conflict);
257 // data->bConflictedActionItem = true;
258 // data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
259 // ++m_nConflicts;
260 // }
261 // else
262 // {
263 // m_bMergesAddsDeletesOccurred = true;
264 data->sActionColumnText.LoadString(IDS_SVNACTION_ADD);
265 data->color = m_Colors.GetColor(CColors::Added);
266 // }
267 break;
268 case git_wc_notify_sendmail:
269 data->sActionColumnText.LoadString(IDS_SVNACTION_SENDMAIL_START);
270 data->color = m_Colors.GetColor(CColors::Modified);
271 break;
273 case git_wc_notify_resolved:
274 data->sActionColumnText.LoadString(IDS_SVNACTION_RESOLVE);
275 break;
277 case git_wc_notify_revert:
278 data->sActionColumnText.LoadString(IDS_SVNACTION_REVERT);
279 break;
281 case git_wc_notify_checkout:
282 data->sActionColumnText.LoadString(IDS_PROGRS_CMD_CHECKOUT);
283 data->color = m_Colors.GetColor(CColors::Added);
284 data->bAuxItem = false;
285 break;
287 #if 0
288 case svn_wc_notify_commit_added:
289 data->sActionColumnText.LoadString(IDS_SVNACTION_ADDING);
290 data->color = m_Colors.GetColor(CColors::Added);
291 break;
292 case svn_wc_notify_copy:
293 data->sActionColumnText.LoadString(IDS_SVNACTION_COPY);
294 break;
295 case svn_wc_notify_commit_modified:
296 data->sActionColumnText.LoadString(IDS_SVNACTION_MODIFIED);
297 data->color = m_Colors.GetColor(CColors::Modified);
298 break;
299 case svn_wc_notify_delete:
300 case svn_wc_notify_update_delete:
301 data->sActionColumnText.LoadString(IDS_SVNACTION_DELETE);
302 m_bMergesAddsDeletesOccurred = true;
303 data->color = m_Colors.GetColor(CColors::Deleted);
304 break;
305 case svn_wc_notify_commit_deleted:
306 data->sActionColumnText.LoadString(IDS_SVNACTION_DELETING);
307 data->color = m_Colors.GetColor(CColors::Deleted);
308 break;
309 case svn_wc_notify_restore:
310 data->sActionColumnText.LoadString(IDS_SVNACTION_RESTORE);
311 break;
313 case svn_wc_notify_update_replace:
314 case svn_wc_notify_commit_replaced:
315 data->sActionColumnText.LoadString(IDS_SVNACTION_REPLACED);
316 data->color = m_Colors.GetColor(CColors::Deleted);
317 break;
318 case svn_wc_notify_exists:
319 if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
321 data->color = m_Colors.GetColor(CColors::Conflict);
322 data->bConflictedActionItem = true;
323 ++m_nConflicts;
324 data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
326 else if ((data->content_state == svn_wc_notify_state_merged) || (data->prop_state == svn_wc_notify_state_merged))
328 data->color = m_Colors.GetColor(CColors::Merged);
329 m_bMergesAddsDeletesOccurred = true;
330 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGED);
332 else
333 data->sActionColumnText.LoadString(IDS_SVNACTION_EXISTS);
334 break;
335 case svn_wc_notify_update_update:
336 // if this is an inoperative dir change, don't show the notification.
337 // an inoperative dir change is when a directory gets updated without
338 // any real change in either text or properties.
339 if ((kind == svn_node_dir)
340 && ((prop_state == svn_wc_notify_state_inapplicable)
341 || (prop_state == svn_wc_notify_state_unknown)
342 || (prop_state == svn_wc_notify_state_unchanged)))
344 bNoNotify = true;
345 break;
347 if ((data->content_state == svn_wc_notify_state_conflicted) || (data->prop_state == svn_wc_notify_state_conflicted))
349 data->color = m_Colors.GetColor(CColors::Conflict);
350 data->bConflictedActionItem = true;
351 ++m_nConflicts;
352 data->sActionColumnText.LoadString(IDS_SVNACTION_CONFLICTED);
354 else if ((data->content_state == svn_wc_notify_state_merged) || (data->prop_state == svn_wc_notify_state_merged))
356 data->color = m_Colors.GetColor(CColors::Merged);
357 m_bMergesAddsDeletesOccurred = true;
358 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGED);
360 else if (((data->content_state != svn_wc_notify_state_unchanged)&&(data->content_state != svn_wc_notify_state_unknown)) ||
361 ((data->prop_state != svn_wc_notify_state_unchanged)&&(data->prop_state != svn_wc_notify_state_unknown)))
363 data->sActionColumnText.LoadString(IDS_SVNACTION_UPDATE);
365 else
367 bNoNotify = true;
368 break;
370 if (lock_state == svn_wc_notify_lock_state_unlocked)
372 CString temp(MAKEINTRESOURCE(IDS_SVNACTION_UNLOCKED));
373 data->sActionColumnText += _T(", ") + temp;
375 break;
377 case svn_wc_notify_update_external:
378 // For some reason we build a list of externals...
379 m_ExtStack.AddHead(path.GetUIPathString());
380 data->sActionColumnText.LoadString(IDS_SVNACTION_EXTERNAL);
381 data->bAuxItem = true;
382 break;
384 case svn_wc_notify_update_completed:
386 data->sActionColumnText.LoadString(IDS_SVNACTION_COMPLETED);
387 data->bAuxItem = true;
388 bool bEmpty = !!m_ExtStack.IsEmpty();
389 if (!bEmpty)
390 data->sPathColumnText.Format(IDS_PROGRS_PATHATREV, (LPCTSTR)m_ExtStack.RemoveHead(), rev);
391 else
392 data->sPathColumnText.Format(IDS_PROGRS_ATREV, rev);
394 if ((m_nConflicts>0)&&(bEmpty))
396 // We're going to add another aux item - let's shove this current onto the list first
397 // I don't really like this, but it will do for the moment.
398 m_arData.push_back(data);
399 AddItemToList();
401 data = new NotificationData();
402 data->bAuxItem = true;
403 data->sActionColumnText.LoadString(IDS_PROGRS_CONFLICTSOCCURED_WARNING);
404 data->sPathColumnText.LoadString(IDS_PROGRS_CONFLICTSOCCURED);
405 data->color = m_Colors.GetColor(CColors::Conflict);
406 CSoundUtils::PlayTSVNWarning();
407 // This item will now be added after the switch statement
409 if (!m_basePath.IsEmpty())
410 m_FinishedRevMap[m_basePath.GetSVNApiPath(pool)] = rev;
411 m_RevisionEnd = rev;
412 m_bFinishedItemAdded = true;
414 break;
415 case svn_wc_notify_commit_postfix_txdelta:
416 data->sActionColumnText.LoadString(IDS_SVNACTION_POSTFIX);
417 break;
418 case svn_wc_notify_failed_revert:
419 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDREVERT);
420 break;
421 case svn_wc_notify_status_completed:
422 case svn_wc_notify_status_external:
423 data->sActionColumnText.LoadString(IDS_SVNACTION_STATUS);
424 break;
425 case svn_wc_notify_skip:
426 if ((content_state == svn_wc_notify_state_missing)||(content_state == svn_wc_notify_state_obstructed)||(content_state == svn_wc_notify_state_conflicted))
428 data->sActionColumnText.LoadString(IDS_SVNACTION_SKIPMISSING);
430 // The color settings dialog describes the red color with
431 // "possible or real conflict / obstructed" which also applies to
432 // skipped targets during a merge. So we just use the same color.
433 data->color = m_Colors.GetColor(CColors::Conflict);
435 else
436 data->sActionColumnText.LoadString(IDS_SVNACTION_SKIP);
437 break;
438 case svn_wc_notify_locked:
439 if ((lock)&&(lock->owner))
440 data->sActionColumnText.Format(IDS_SVNACTION_LOCKEDBY, (LPCTSTR)CUnicodeUtils::GetUnicode(lock->owner));
441 break;
442 case svn_wc_notify_unlocked:
443 data->sActionColumnText.LoadString(IDS_SVNACTION_UNLOCKED);
444 break;
445 case svn_wc_notify_failed_lock:
446 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDLOCK);
447 m_arData.push_back(data);
448 AddItemToList();
449 ReportError(SVN::GetErrorString(err));
450 bDoAddData = false;
451 if (err->apr_err == SVN_ERR_FS_OUT_OF_DATE)
452 m_bLockWarning = true;
453 if (err->apr_err == SVN_ERR_FS_PATH_ALREADY_LOCKED)
454 m_bLockExists = true;
455 break;
456 case svn_wc_notify_failed_unlock:
457 data->sActionColumnText.LoadString(IDS_SVNACTION_FAILEDUNLOCK);
458 m_arData.push_back(data);
459 AddItemToList();
460 ReportError(SVN::GetErrorString(err));
461 bDoAddData = false;
462 if (err->apr_err == SVN_ERR_FS_OUT_OF_DATE)
463 m_bLockWarning = true;
464 break;
465 case svn_wc_notify_changelist_set:
466 data->sActionColumnText.Format(IDS_SVNACTION_CHANGELISTSET, (LPCTSTR)data->changelistname);
467 break;
468 case svn_wc_notify_changelist_clear:
469 data->sActionColumnText.LoadString(IDS_SVNACTION_CHANGELISTCLEAR);
470 break;
471 case svn_wc_notify_changelist_moved:
472 data->sActionColumnText.Format(IDS_SVNACTION_CHANGELISTMOVED, (LPCTSTR)data->changelistname);
473 break;
474 case svn_wc_notify_foreign_merge_begin:
475 case svn_wc_notify_merge_begin:
476 if (range == NULL)
477 data->sActionColumnText.LoadString(IDS_SVNACTION_MERGEBEGINNONE);
478 else if ((data->merge_range.start == data->merge_range.end) || (data->merge_range.start == data->merge_range.end - 1))
479 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINSINGLE, data->merge_range.end);
480 else if (data->merge_range.start - 1 == data->merge_range.end)
481 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINSINGLEREVERSE, data->merge_range.start);
482 else if (data->merge_range.start < data->merge_range.end)
483 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINMULTIPLE, data->merge_range.start + 1, data->merge_range.end);
484 else
485 data->sActionColumnText.Format(IDS_SVNACTION_MERGEBEGINMULTIPLEREVERSE, data->merge_range.start, data->merge_range.end + 1);
486 data->bAuxItem = true;
487 break;
488 #endif
489 default:
490 break;
491 } // switch (data->action)
493 if (bNoNotify)
494 delete data;
495 else
497 if (bDoAddData)
499 m_arData.push_back(data);
500 AddItemToList();
501 if ((!data->bAuxItem) && (m_itemCount > 0))
503 if (m_pProgControl)
505 m_pProgControl->ShowWindow(SW_SHOW);
506 m_pProgControl->SetPos(m_itemCount);
507 m_pProgControl->SetRange32(0, m_itemCountTotal);
509 if (m_pTaskbarList && m_pPostWnd)
511 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_NORMAL);
512 m_pTaskbarList->SetProgressValue(m_pPostWnd->GetSafeHwnd(), m_itemCount, m_itemCountTotal);
516 //if ((action == svn_wc_notify_commit_postfix_txdelta)&&(bSecondResized == FALSE))
518 // ResizeColumns();
519 // bSecondResized = TRUE;
523 return TRUE;
527 CString CGitProgressList::BuildInfoString()
529 CString infotext;
530 if(this->m_Command == GitProgress_Resolve)
531 infotext = _T("You need commit your change after resolve conflict");
532 #if 0
534 CString temp;
535 int added = 0;
536 int copied = 0;
537 int deleted = 0;
538 int restored = 0;
539 int reverted = 0;
540 int resolved = 0;
541 int conflicted = 0;
542 int updated = 0;
543 int merged = 0;
544 int modified = 0;
545 int skipped = 0;
546 int replaced = 0;
548 for (size_t i=0; i<m_arData.size(); ++i)
550 const NotificationData * dat = m_arData[i];
551 switch (dat->action)
553 case svn_wc_notify_add:
554 case svn_wc_notify_update_add:
555 case svn_wc_notify_commit_added:
556 if (dat->bConflictedActionItem)
557 ++conflicted;
558 else
559 ++added;
560 break;
561 case svn_wc_notify_copy:
562 ++copied;
563 break;
564 case svn_wc_notify_delete:
565 case svn_wc_notify_update_delete:
566 case svn_wc_notify_commit_deleted:
567 ++deleted;
568 break;
569 case svn_wc_notify_restore:
570 ++restored;
571 break;
572 case svn_wc_notify_revert:
573 ++reverted;
574 break;
575 case svn_wc_notify_resolved:
576 ++resolved;
577 break;
578 case svn_wc_notify_update_update:
579 if (dat->bConflictedActionItem)
580 ++conflicted;
581 else if ((dat->content_state == svn_wc_notify_state_merged) || (dat->prop_state == svn_wc_notify_state_merged))
582 ++merged;
583 else
584 ++updated;
585 break;
586 case svn_wc_notify_commit_modified:
587 ++modified;
588 break;
589 case svn_wc_notify_skip:
590 ++skipped;
591 break;
592 case svn_wc_notify_commit_replaced:
593 ++replaced;
594 break;
597 if (conflicted)
599 temp.LoadString(IDS_SVNACTION_CONFLICTED);
600 infotext += temp;
601 temp.Format(_T(":%d "), conflicted);
602 infotext += temp;
604 if (skipped)
606 temp.LoadString(IDS_SVNACTION_SKIP);
607 infotext += temp;
608 infotext.AppendFormat(_T(":%d "), skipped);
610 if (merged)
612 temp.LoadString(IDS_SVNACTION_MERGED);
613 infotext += temp;
614 infotext.AppendFormat(_T(":%d "), merged);
616 if (added)
618 temp.LoadString(IDS_SVNACTION_ADD);
619 infotext += temp;
620 infotext.AppendFormat(_T(":%d "), added);
622 if (deleted)
624 temp.LoadString(IDS_SVNACTION_DELETE);
625 infotext += temp;
626 infotext.AppendFormat(_T(":%d "), deleted);
628 if (modified)
630 temp.LoadString(IDS_SVNACTION_MODIFIED);
631 infotext += temp;
632 infotext.AppendFormat(_T(":%d "), modified);
634 if (copied)
636 temp.LoadString(IDS_SVNACTION_COPY);
637 infotext += temp;
638 infotext.AppendFormat(_T(":%d "), copied);
640 if (replaced)
642 temp.LoadString(IDS_SVNACTION_REPLACED);
643 infotext += temp;
644 infotext.AppendFormat(_T(":%d "), replaced);
646 if (updated)
648 temp.LoadString(IDS_SVNACTION_UPDATE);
649 infotext += temp;
650 infotext.AppendFormat(_T(":%d "), updated);
652 if (restored)
654 temp.LoadString(IDS_SVNACTION_RESTORE);
655 infotext += temp;
656 infotext.AppendFormat(_T(":%d "), restored);
658 if (reverted)
660 temp.LoadString(IDS_SVNACTION_REVERT);
661 infotext += temp;
662 infotext.AppendFormat(_T(":%d "), reverted);
664 if (resolved)
666 temp.LoadString(IDS_SVNACTION_RESOLVE);
667 infotext += temp;
668 infotext.AppendFormat(_T(":%d "), resolved);
670 #endif
671 return infotext;
674 void CGitProgressList::SetSelectedList(const CTGitPathList& selPaths)
676 m_selectedPaths = selPaths;
679 void CGitProgressList::ResizeColumns()
681 SetRedraw(FALSE);
683 TCHAR textbuf[MAX_PATH];
685 int maxcol = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
686 for (int col = 0; col <= maxcol; ++col)
688 // find the longest width of all items
689 int count = GetItemCount();
690 HDITEM hdi = {0};
691 hdi.mask = HDI_TEXT;
692 hdi.pszText = textbuf;
693 hdi.cchTextMax = sizeof(textbuf);
694 ((CHeaderCtrl*)(GetDlgItem(0)))->GetItem(col, &hdi);
695 int cx = GetStringWidth(hdi.pszText)+20; // 20 pixels for col separator and margin
697 for (int index = 0; index<count; ++index)
699 // get the width of the string and add 12 pixels for the column separator and margins
700 int linewidth = cx;
701 switch (col)
703 case 0:
704 linewidth = GetStringWidth(m_arData[index]->sActionColumnText) + 12;
705 break;
706 case 1:
707 linewidth = GetStringWidth(m_arData[index]->sPathColumnText) + 12;
708 break;
710 if (cx < linewidth)
711 cx = linewidth;
713 SetColumnWidth(col, cx);
716 SetRedraw(TRUE);
719 bool CGitProgressList::SetBackgroundImage(UINT nID)
721 return CAppUtils::SetListCtrlBackgroundImage(GetSafeHwnd(), nID);
724 void CGitProgressList::ReportGitError()
726 ReportError(CGit::GetLibGit2LastErr());
729 void CGitProgressList::ReportUserCanceled()
731 ReportError(CString(MAKEINTRESOURCE(IDS_SVN_USERCANCELLED)));
734 void CGitProgressList::ReportError(const CString& sError)
736 CSoundUtils::PlayTGitError();
737 ReportString(sError, CString(MAKEINTRESOURCE(IDS_ERR_ERROR)), m_Colors.GetColor(CColors::Conflict));
738 m_bErrorsOccurred = true;
741 void CGitProgressList::ReportWarning(const CString& sWarning)
743 CSoundUtils::PlayTGitWarning();
744 ReportString(sWarning, CString(MAKEINTRESOURCE(IDS_WARN_WARNING)), m_Colors.GetColor(CColors::Conflict));
747 void CGitProgressList::ReportNotification(const CString& sNotification)
749 CSoundUtils::PlayTGitNotification();
750 ReportString(sNotification, CString(MAKEINTRESOURCE(IDS_WARN_NOTE)));
753 void CGitProgressList::ReportCmd(const CString& sCmd)
755 ReportString(sCmd, CString(MAKEINTRESOURCE(IDS_PROGRS_CMDINFO)), m_Colors.GetColor(CColors::Cmd));
758 void CGitProgressList::ReportString(CString sMessage, const CString& sMsgKind, COLORREF color)
760 // instead of showing a dialog box with the error message or notification,
761 // just insert the error text into the list control.
762 // that way the user isn't 'interrupted' by a dialog box popping up!
764 // the message may be split up into different lines
765 // so add a new entry for each line of the message
766 while (!sMessage.IsEmpty())
768 NotificationData * data = new NotificationData();
769 data->bAuxItem = true;
770 data->sActionColumnText = sMsgKind;
771 if (sMessage.Find('\n')>=0)
772 data->sPathColumnText = sMessage.Left(sMessage.Find('\n'));
773 else
774 data->sPathColumnText = sMessage;
775 data->sPathColumnText.Trim(_T("\n\r"));
776 data->color = color;
777 if (sMessage.Find('\n')>=0)
779 sMessage = sMessage.Mid(sMessage.Find('\n'));
780 sMessage.Trim(_T("\n\r"));
782 else
783 sMessage.Empty();
784 m_arData.push_back(data);
785 AddItemToList();
789 UINT CGitProgressList::ProgressThreadEntry(LPVOID pVoid)
791 return ((CGitProgressList*)pVoid)->ProgressThread();
794 UINT CGitProgressList::ProgressThread()
796 // The SetParams function should have loaded something for us
798 CString temp;
799 CString sWindowTitle;
800 bool localoperation = false;
801 bool bSuccess = false;
802 m_AlwaysConflicted = false;
804 if(m_pPostWnd)
805 m_pPostWnd->PostMessage(WM_PROG_CMD_START, m_Command);
807 if(m_pProgressLabelCtrl)
809 m_pProgressLabelCtrl->ShowWindow(SW_SHOW);
810 m_pProgressLabelCtrl->SetWindowText(_T(""));
813 // SetAndClearProgressInfo(m_hWnd);
814 m_itemCount = m_itemCountTotal;
816 InterlockedExchange(&m_bThreadRunning, TRUE);
817 iFirstResized = 0;
818 bSecondResized = FALSE;
819 m_bFinishedItemAdded = false;
820 DWORD startTime = GetCurrentTime();
822 if (m_pTaskbarList && m_pPostWnd)
823 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_INDETERMINATE);
825 switch (m_Command)
827 case GitProgress_Add:
828 bSuccess = CmdAdd(sWindowTitle, localoperation);
829 break;
830 case GitProgress_Copy:
831 bSuccess = CmdCopy(sWindowTitle, localoperation);
832 break;
833 case GitProgress_Export:
834 bSuccess = CmdExport(sWindowTitle, localoperation);
835 break;
836 case GitProgress_Rename:
837 bSuccess = CmdRename(sWindowTitle, localoperation);
838 break;
839 case GitProgress_Resolve:
840 bSuccess = CmdResolve(sWindowTitle, localoperation);
841 break;
842 case GitProgress_Revert:
843 bSuccess = CmdRevert(sWindowTitle, localoperation);
844 break;
845 case GitProgress_Switch:
846 bSuccess = CmdSwitch(sWindowTitle, localoperation);
847 break;
848 case GitProgress_SendMail:
849 bSuccess = CmdSendMail(sWindowTitle, localoperation);
850 break;
851 case GitProgress_Clone:
852 bSuccess = CmdClone(sWindowTitle, localoperation);
853 break;
854 case GitProgress_Fetch:
855 bSuccess = CmdFetch(sWindowTitle, localoperation);
856 break;
858 if (!bSuccess)
859 temp.LoadString(IDS_PROGRS_TITLEFAILED);
860 else
861 temp.LoadString(IDS_PROGRS_TITLEFIN);
862 sWindowTitle = sWindowTitle + _T(" ") + temp;
863 if (m_bSetTitle && m_pPostWnd)
864 ::SetWindowText(m_pPostWnd->GetSafeHwnd(), sWindowTitle);
866 KillTimer(TRANSFERTIMER);
867 KillTimer(VISIBLETIMER);
869 if (m_pTaskbarList && m_pPostWnd)
871 if (DidErrorsOccur())
873 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_ERROR);
874 m_pTaskbarList->SetProgressValue(m_pPostWnd->GetSafeHwnd(), 100, 100);
876 else
877 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_NOPROGRESS);
880 CString info = BuildInfoString();
881 if (!bSuccess)
882 info.LoadString(IDS_PROGRS_INFOFAILED);
883 if (m_pInfoCtrl)
884 m_pInfoCtrl->SetWindowText(info);
886 ResizeColumns();
888 DWORD time = GetCurrentTime() - startTime;
890 CString sFinalInfo;
891 if (!m_sTotalBytesTransferred.IsEmpty())
893 temp.Format(IDS_PROGRS_TIME, (time / 1000) / 60, (time / 1000) % 60);
894 sFinalInfo.Format(IDS_PROGRS_FINALINFO, m_sTotalBytesTransferred, (LPCTSTR)temp);
895 if (m_pProgressLabelCtrl)
896 m_pProgressLabelCtrl->SetWindowText(sFinalInfo);
898 else
900 if (m_pProgressLabelCtrl)
901 m_pProgressLabelCtrl->ShowWindow(SW_HIDE);
904 if (m_pProgControl)
905 m_pProgControl->ShowWindow(SW_HIDE);
907 if (!m_bFinishedItemAdded)
909 CString log, str;
910 if (bSuccess)
911 str.LoadString(IDS_SUCCESS);
912 else
913 str.LoadString(IDS_FAIL);
914 log.Format(_T("%s (%d ms @ %s)"), str, time, CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false));
916 // there's no "finished: xxx" line at the end. We add one here to make
917 // sure the user sees that the command is actually finished.
918 ReportString(log, CString(MAKEINTRESOURCE(IDS_PROGRS_FINISHED)), bSuccess? RGB(0,0,255) : RGB(255,0,0));
921 int count = GetItemCount();
922 if ((count > 0)&&(m_bLastVisible))
923 EnsureVisible(count-1, FALSE);
925 CLogFile logfile;
926 if (logfile.Open())
928 logfile.AddTimeLine();
929 for (size_t i = 0; i < m_arData.size(); ++i)
931 NotificationData * data = m_arData[i];
932 temp.Format(_T("%-20s : %s"), (LPCTSTR)data->sActionColumnText, (LPCTSTR)data->sPathColumnText);
933 logfile.AddLine(temp);
935 if (!sFinalInfo.IsEmpty())
936 logfile.AddLine(sFinalInfo);
937 logfile.Close();
940 m_bCancelled = TRUE;
941 InterlockedExchange(&m_bThreadRunning, FALSE);
942 #if 0 //need
943 RefreshCursor();
944 #endif
946 if (m_pPostWnd)
947 m_pPostWnd->PostMessage(WM_PROG_CMD_FINISH, this->m_Command, 0L);
949 #if 0
950 DWORD dwAutoClose = CRegStdDWORD(_T("Software\\TortoiseGit\\AutoClose"), CLOSE_MANUAL);
951 if (m_options & ProgOptDryRun)
952 dwAutoClose = 0; // dry run means progress dialog doesn't auto close at all
953 if (!m_bLastVisible)
954 dwAutoClose = 0;
955 if (m_dwCloseOnEnd != (DWORD)-1)
956 dwAutoClose = m_dwCloseOnEnd; // command line value has priority over setting value
957 if ((dwAutoClose == CLOSE_NOERRORS)&&(!m_bErrorsOccurred))
958 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
959 if ((dwAutoClose == CLOSE_NOCONFLICTS)&&(!m_bErrorsOccurred)&&(m_nConflicts==0))
960 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
961 if ((dwAutoClose == CLOSE_NOMERGES)&&(!m_bErrorsOccurred)&&(m_nConflicts==0)&&(!m_bMergesAddsDeletesOccurred))
962 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
963 if ((dwAutoClose == CLOSE_LOCAL)&&(!m_bErrorsOccurred)&&(m_nConflicts==0)&&(localoperation))
964 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
965 #endif
967 //Don't do anything here which might cause messages to be sent to the window
968 //The window thread is probably now blocked in OnOK if we've done an auto close
969 return 0;
972 void CGitProgressList::OnLvnGetdispinfoSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
974 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
976 if (pDispInfo)
978 if (pDispInfo->item.mask & LVIF_TEXT)
980 if (pDispInfo->item.iItem < (int)m_arData.size())
982 const NotificationData * data = m_arData[pDispInfo->item.iItem];
983 switch (pDispInfo->item.iSubItem)
985 case 0:
986 lstrcpyn(m_columnbuf, data->sActionColumnText, MAX_PATH);
987 break;
988 case 1:
989 lstrcpyn(m_columnbuf, data->sPathColumnText, pDispInfo->item.cchTextMax);
990 if (!data->bAuxItem)
992 int cWidth = GetColumnWidth(1);
993 cWidth = max(12, cWidth-12);
994 CDC * pDC = GetDC();
995 if (pDC != NULL)
997 CFont * pFont = pDC->SelectObject(GetFont());
998 PathCompactPath(pDC->GetSafeHdc(), m_columnbuf, cWidth);
999 pDC->SelectObject(pFont);
1000 ReleaseDC(pDC);
1003 break;
1004 default:
1005 m_columnbuf[0] = 0;
1007 pDispInfo->item.pszText = m_columnbuf;
1011 *pResult = 0;
1014 void CGitProgressList::OnNMCustomdrawSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
1016 NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
1018 // Take the default processing unless we set this to something else below.
1019 *pResult = CDRF_DODEFAULT;
1021 // First thing - check the draw stage. If it's the control's prepaint
1022 // stage, then tell Windows we want messages for every item.
1024 if ( CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage )
1026 *pResult = CDRF_NOTIFYITEMDRAW;
1028 else if ( CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage )
1030 // This is the prepaint stage for an item. Here's where we set the
1031 // item's text color. Our return value will tell Windows to draw the
1032 // item itself, but it will use the new color we set here.
1034 // Tell Windows to paint the control itself.
1035 *pResult = CDRF_DODEFAULT;
1037 ASSERT(pLVCD->nmcd.dwItemSpec < m_arData.size());
1038 if(pLVCD->nmcd.dwItemSpec >= m_arData.size())
1040 return;
1042 const NotificationData * data = m_arData[pLVCD->nmcd.dwItemSpec];
1043 ASSERT(data != NULL);
1044 if (data == NULL)
1045 return;
1047 // Store the color back in the NMLVCUSTOMDRAW struct.
1048 pLVCD->clrText = data->color;
1052 void CGitProgressList::OnNMDblclkSvnprogress(NMHDR * /*pNMHDR*/, LRESULT * /*pResult*/)
1054 #if 0
1055 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1056 *pResult = 0;
1057 if (pNMLV->iItem < 0)
1058 return;
1059 if (m_options & ProgOptDryRun)
1060 return; //don't do anything in a dry-run.
1062 const NotificationData * data = m_arData[pNMLV->iItem];
1063 if (data == NULL)
1064 return;
1066 if (data->bConflictedActionItem)
1068 // We've double-clicked on a conflicted item - do a three-way merge on it
1069 SVNDiff::StartConflictEditor(data->path);
1071 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))
1073 // This is a modified file which has been merged on update. Diff it against base
1074 CTGitPath temporaryFile;
1075 SVNDiff diff(this, this->m_hWnd, true);
1076 diff.SetAlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000));
1077 svn_revnum_t baseRev = 0;
1078 diff.DiffFileAgainstBase(data->path, baseRev);
1080 else if ((!data->bAuxItem)&&(data->path.Exists())&&(!data->path.IsDirectory()))
1082 bool bOpenWith = false;
1083 int ret = (int)ShellExecute(m_hWnd, NULL, data->path.GetWinPath(), NULL, NULL, SW_SHOWNORMAL);
1084 if (ret <= HINSTANCE_ERROR)
1085 bOpenWith = true;
1086 if (bOpenWith)
1088 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1089 cmd += data->path.GetWinPathString() + _T(" ");
1090 CAppUtils::LaunchApplication(cmd, NULL, false);
1093 #endif
1096 void CGitProgressList::OnHdnItemclickSvnprogress(NMHDR *pNMHDR, LRESULT *pResult)
1098 LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
1099 if (m_bThreadRunning)
1100 return;
1101 if (m_nSortedColumn == phdr->iItem)
1102 m_bAscending = !m_bAscending;
1103 else
1104 m_bAscending = TRUE;
1105 m_nSortedColumn = phdr->iItem;
1106 Sort();
1108 CString temp;
1109 SetRedraw(FALSE);
1110 DeleteAllItems();
1111 SetItemCountEx (static_cast<int>(m_arData.size()));
1113 SetRedraw(TRUE);
1115 *pResult = 0;
1118 bool CGitProgressList::NotificationDataIsAux(const NotificationData* pData)
1120 return pData->bAuxItem;
1122 BOOL CGitProgressList::Notify(const git_wc_notify_action_t action, CString str, const git_oid *a, const git_oid *b)
1124 NotificationData * data = new NotificationData();
1125 data->action = action;
1126 data->bAuxItem = false;
1128 if (action == git_wc_notify_update_ref)
1130 data->m_NewHash = b->id;
1131 data->m_OldHash = a->id;
1132 data->sActionColumnText.LoadString(IDS_GITACTION_UPDATE_REF);
1133 data->sPathColumnText.Format(_T("%s\t %s -> %s"), str,
1134 data->m_OldHash.ToString().Left(g_Git.GetShortHASHLength()),
1135 data->m_NewHash.ToString().Left(g_Git.GetShortHASHLength()));
1139 m_arData.push_back(data);
1140 AddItemToList();
1142 if (m_pAnimate)
1143 m_pAnimate->Stop();
1144 if (m_pAnimate)
1145 m_pAnimate->ShowWindow(SW_HIDE);
1146 return TRUE;
1148 BOOL CGitProgressList::Notify(const git_wc_notify_action_t /*action*/, const git_transfer_progress *stat)
1150 static unsigned int start = 0;
1151 unsigned int dt = GetCurrentTime() - start;
1152 size_t ds;
1153 double speed = 0;
1155 if (m_bCancelled)
1156 return FALSE;
1158 if (dt > 100)
1160 start = GetCurrentTime();
1161 ds = stat->received_bytes - m_TotalBytesTransferred;
1162 speed = ds * 1000.0/dt;
1163 m_TotalBytesTransferred = stat->received_bytes;
1165 else
1167 return TRUE;
1170 int progress;
1171 progress = stat->received_objects + stat->indexed_objects;
1173 if ((stat->total_objects > 1000) && m_pProgControl && (!m_pProgControl->IsWindowVisible()))
1175 if (m_pProgControl)
1176 m_pProgControl->ShowWindow(SW_SHOW);
1179 if (m_pProgressLabelCtrl && m_pProgressLabelCtrl->IsWindowVisible())
1180 m_pProgressLabelCtrl->ShowWindow(SW_SHOW);
1182 if (m_pProgControl)
1184 m_pProgControl->SetPos(progress);
1185 m_pProgControl->SetRange32(0, 2 * stat->total_objects);
1187 if (m_pTaskbarList && m_pPostWnd)
1189 m_pTaskbarList->SetProgressState(m_pPostWnd->GetSafeHwnd(), TBPF_NORMAL);
1190 m_pTaskbarList->SetProgressValue(m_pPostWnd->GetSafeHwnd(), progress, 2 * stat->total_objects);
1193 CString progText;
1194 if (stat->received_bytes < 1024)
1195 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALBYTESTRANSFERRED, (int64_t)stat->received_bytes);
1196 else if (stat->received_bytes < 1200000)
1197 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALTRANSFERRED, (int64_t)stat->received_bytes / 1024);
1198 else
1199 m_sTotalBytesTransferred.Format(IDS_SVN_PROGRESS_TOTALMBTRANSFERRED, (double)((double)stat->received_bytes / 1024000.0));
1201 CString str;
1202 if(speed < 1024)
1203 str.Format(_T("%fB/s"), speed);
1204 else if(speed < 1024 * 1024)
1205 str.Format(_T("%.2fKB/s"), speed / 1024);
1206 else
1207 str.Format(_T("%.2fMB/s"), speed / 1024000.0);
1209 progText.Format(IDS_SVN_PROGRESS_TOTALANDSPEED, (LPCTSTR)m_sTotalBytesTransferred, (LPCTSTR)str);
1210 if (m_pProgressLabelCtrl)
1211 m_pProgressLabelCtrl->SetWindowText(progText);
1213 return TRUE;
1216 void CGitProgressList::OnTimer(UINT_PTR nIDEvent)
1218 if (nIDEvent == TRANSFERTIMER)
1220 CString progText;
1221 CString progSpeed;
1222 progSpeed.Format(IDS_SVN_PROGRESS_BYTES_SEC, 0);
1223 progText.Format(IDS_SVN_PROGRESS_TOTALANDSPEED, (LPCTSTR)m_sTotalBytesTransferred, (LPCTSTR)progSpeed);
1224 if (m_pProgressLabelCtrl)
1225 m_pProgressLabelCtrl->SetWindowText(progText);
1227 KillTimer(TRANSFERTIMER);
1229 if (nIDEvent == VISIBLETIMER)
1231 if (nEnsureVisibleCount)
1232 EnsureVisible(GetItemCount()-1, false);
1233 nEnsureVisibleCount = 0;
1237 void CGitProgressList::Sort()
1239 if(m_arData.size() < 2)
1241 return;
1244 // We need to sort the blocks which lie between the auxiliary entries
1245 // This is so that any aux data stays where it was
1246 NotificationDataVect::iterator actionBlockBegin;
1247 NotificationDataVect::iterator actionBlockEnd = m_arData.begin(); // We start searching from here
1249 for(;;)
1251 // Search to the start of the non-aux entry in the next block
1252 actionBlockBegin = std::find_if(actionBlockEnd, m_arData.end(), std::not1(std::ptr_fun(&CGitProgressList::NotificationDataIsAux)));
1253 if(actionBlockBegin == m_arData.end())
1255 // There are no more actions
1256 break;
1258 // Now search to find the end of the block
1259 actionBlockEnd = std::find_if(actionBlockBegin+1, m_arData.end(), std::ptr_fun(&CGitProgressList::NotificationDataIsAux));
1260 // Now sort the block
1261 std::sort(actionBlockBegin, actionBlockEnd, &CGitProgressList::SortCompare);
1265 bool CGitProgressList::SortCompare(const NotificationData * pData1, const NotificationData * pData2)
1267 int result = 0;
1268 switch (m_nSortedColumn)
1270 case 0: //action column
1271 result = pData1->sActionColumnText.Compare(pData2->sActionColumnText);
1272 break;
1273 case 1: //path column
1274 // Compare happens after switch()
1275 break;
1276 default:
1277 break;
1280 // Sort by path if everything else is equal
1281 if (result == 0)
1283 result = CTGitPath::Compare(pData1->path, pData2->path);
1286 if (!m_bAscending)
1287 result = -result;
1288 return result < 0;
1291 void CGitProgressList::OnContextMenu(CWnd* pWnd, CPoint point)
1293 if (m_options & ProgOptDryRun)
1294 return; // don't do anything in a dry-run.
1296 if (pWnd == this)
1298 int selIndex = GetSelectionMark();
1299 if ((point.x == -1) && (point.y == -1))
1301 // Menu was invoked from the keyboard rather than by right-clicking
1302 CRect rect;
1303 GetItemRect(selIndex, &rect, LVIR_LABEL);
1304 ClientToScreen(&rect);
1305 point = rect.CenterPoint();
1308 if ((selIndex >= 0)&&(!m_bThreadRunning))
1310 // entry is selected, thread has finished with updating so show the popup menu
1311 CIconMenu popup;
1312 if (popup.CreatePopupMenu())
1314 bool bAdded = false;
1315 NotificationData * data = m_arData[selIndex];
1316 if ((data)&&(!data->path.IsDirectory()))
1319 if (data->action == svn_wc_notify_update_update || data->action == svn_wc_notify_resolved)
1321 if (GetSelectedCount() == 1)
1323 popup.AppendMenuIcon(ID_COMPARE, IDS_LOG_POPUP_COMPARE, IDI_DIFF);
1324 bAdded = true;
1328 if (data->bConflictedActionItem)
1330 if (GetSelectedCount() == 1)
1332 popup.AppendMenuIcon(ID_EDITCONFLICT, IDS_MENUCONFLICT,IDI_CONFLICT);
1333 popup.SetDefaultItem(ID_EDITCONFLICT, FALSE);
1334 popup.AppendMenuIcon(ID_CONFLICTRESOLVE, IDS_SVNPROGRESS_MENUMARKASRESOLVED,IDI_RESOLVE);
1336 popup.AppendMenuIcon(ID_CONFLICTUSETHEIRS, IDS_SVNPROGRESS_MENUUSETHEIRS,IDI_RESOLVE);
1337 popup.AppendMenuIcon(ID_CONFLICTUSEMINE, IDS_SVNPROGRESS_MENUUSEMINE,IDI_RESOLVE);
1339 else if ((data->content_state == svn_wc_notify_state_merged)||(GitProgress_Merge == m_Command)||(data->action == svn_wc_notify_resolved))
1340 popup.SetDefaultItem(ID_COMPARE, FALSE);
1342 if (GetSelectedCount() == 1)
1344 if ((data->action == git_wc_notify_add)||
1345 (data->action == git_wc_notify_revert)||
1346 (data->action == git_wc_notify_resolved)||
1347 (data->action == git_wc_notify_checkout)||
1348 (data->action == git_wc_notify_update_ref))
1350 popup.AppendMenuIcon(ID_LOG, IDS_MENULOG,IDI_LOG);
1351 popup.AppendMenu(MF_SEPARATOR, NULL);
1352 popup.AppendMenuIcon(ID_OPEN, IDS_LOG_POPUP_OPEN, IDI_OPEN);
1353 popup.AppendMenuIcon(ID_OPENWITH, IDS_LOG_POPUP_OPENWITH, IDI_OPEN);
1354 bAdded = true;
1357 } // if ((data)&&(!data->path.IsDirectory()))
1358 if (GetSelectedCount() == 1)
1360 if (data)
1362 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1363 CTGitPath path = CTGitPath(sPath);
1364 if (!sPath.IsEmpty())
1366 if (path.GetDirectory().Exists())
1368 popup.AppendMenuIcon(ID_EXPLORE, IDS_SVNPROGRESS_MENUOPENPARENT, IDI_EXPLORER);
1369 bAdded = true;
1374 if (GetSelectedCount() > 0)
1376 if (bAdded)
1377 popup.AppendMenu(MF_SEPARATOR, NULL);
1378 popup.AppendMenuIcon(ID_COPY, IDS_LOG_POPUP_COPYTOCLIPBOARD,IDI_COPYCLIP);
1379 bAdded = true;
1381 if (bAdded)
1383 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, point.x, point.y, this, 0);
1384 #if 0//need
1385 DialogEnableWindow(IDOK, FALSE);
1386 #endif
1387 //this->SetPromptApp(&theApp);
1388 theApp.DoWaitCursor(1);
1389 bool bOpenWith = false;
1390 switch (cmd)
1392 case ID_COPY:
1394 CString sLines;
1395 POSITION pos = GetFirstSelectedItemPosition();
1396 while (pos)
1398 int nItem = GetNextSelectedItem(pos);
1399 NotificationData * data = m_arData[nItem];
1400 if (data)
1402 sLines += data->sPathColumnText;
1403 sLines += _T("\r\n");
1406 sLines.TrimRight();
1407 if (!sLines.IsEmpty())
1409 CStringUtils::WriteAsciiStringToClipboard(sLines, GetSafeHwnd());
1412 break;
1413 case ID_EXPLORE:
1415 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1417 CTGitPath path = CTGitPath(sPath);
1418 ShellExecute(m_hWnd, _T("explore"), path.GetDirectory().GetWinPath(), NULL, path.GetDirectory().GetWinPath(), SW_SHOW);
1420 break;
1421 #if 0
1422 case ID_COMPARE:
1424 svn_revnum_t rev = -1;
1425 StringRevMap::iterator it = m_UpdateStartRevMap.end();
1426 if (data->basepath.IsEmpty())
1427 it = m_UpdateStartRevMap.begin();
1428 else
1429 it = m_UpdateStartRevMap.find(data->basepath.GetSVNApiPath(pool));
1430 if (it != m_UpdateStartRevMap.end())
1431 rev = it->second;
1432 // if the file was merged during update, do a three way diff between OLD, MINE, THEIRS
1433 if (data->content_state == svn_wc_notify_state_merged)
1435 CTGitPath basefile = CTempFiles::Instance().GetTempFilePath(false, data->path, rev);
1436 CTGitPath newfile = CTempFiles::Instance().GetTempFilePath(false, data->path, SVNRev::REV_HEAD);
1437 SVN svn;
1438 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_WC), rev, basefile))
1440 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1441 DialogEnableWindow(IDOK, TRUE);
1442 break;
1444 // If necessary, convert the line-endings on the file before diffing
1445 if ((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\ConvertBase"), TRUE))
1447 CTGitPath temporaryFile = CTempFiles::Instance().GetTempFilePath(false, data->path, SVNRev::REV_BASE);
1448 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_BASE), SVNRev(SVNRev::REV_BASE), temporaryFile))
1450 temporaryFile.Reset();
1451 break;
1453 else
1455 newfile = temporaryFile;
1459 SetFileAttributes(newfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1460 SetFileAttributes(basefile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1461 CString revname, wcname, basename;
1462 revname.Format(_T("%s Revision %ld"), (LPCTSTR)data->path.GetUIFileOrDirectoryName(), rev);
1463 wcname.Format(IDS_DIFF_WCNAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1464 basename.Format(IDS_DIFF_BASENAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1465 CAppUtils::StartExtMerge(basefile, newfile, data->path, data->path, basename, revname, wcname, CString(), true);
1467 else
1469 CTGitPath tempfile = CTempFiles::Instance().GetTempFilePath(false, data->path, rev);
1470 SVN svn;
1471 if (!svn.Cat(data->path, SVNRev(SVNRev::REV_WC), rev, tempfile))
1473 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1474 DialogEnableWindow(IDOK, TRUE);
1475 break;
1477 else
1479 SetFileAttributes(tempfile.GetWinPath(), FILE_ATTRIBUTE_READONLY);
1480 CString revname, wcname;
1481 revname.Format(_T("%s Revision %ld"), (LPCTSTR)data->path.GetUIFileOrDirectoryName(), rev);
1482 wcname.Format(IDS_DIFF_WCNAME, (LPCTSTR)data->path.GetUIFileOrDirectoryName());
1483 CAppUtils::StartExtDiff(
1484 tempfile, data->path, revname, wcname,
1485 CAppUtils::DiffFlags().AlternativeTool(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000)));
1489 break;
1490 case ID_EDITCONFLICT:
1492 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1493 SVNDiff::StartConflictEditor(CTGitPath(sPath));
1495 break;
1496 case ID_CONFLICTUSETHEIRS:
1497 case ID_CONFLICTUSEMINE:
1498 case ID_CONFLICTRESOLVE:
1500 svn_wc_conflict_choice_t result = svn_wc_conflict_choose_merged;
1501 switch (cmd)
1503 case ID_CONFLICTUSETHEIRS:
1504 result = svn_wc_conflict_choose_theirs_full;
1505 break;
1506 case ID_CONFLICTUSEMINE:
1507 result = svn_wc_conflict_choose_mine_full;
1508 break;
1509 case ID_CONFLICTRESOLVE:
1510 result = svn_wc_conflict_choose_merged;
1511 break;
1513 SVN svn;
1514 POSITION pos = GetFirstSelectedItemPosition();
1515 CString sResolvedPaths;
1516 while (pos)
1518 int nItem = GetNextSelectedItem(pos);
1519 NotificationData * data = m_arData[nItem];
1520 if (data)
1522 if (data->bConflictedActionItem)
1524 if (!svn.Resolve(data->path, result, FALSE))
1526 CMessageBox::Show(m_hWnd, svn.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);
1527 DialogEnableWindow(IDOK, TRUE);
1528 break;
1530 else
1532 data->color = ::GetSysColor(COLOR_WINDOWTEXT);
1533 data->action = svn_wc_notify_resolved;
1534 data->sActionColumnText.LoadString(IDS_SVNACTION_RESOLVE);
1535 data->bConflictedActionItem = false;
1536 m_nConflicts--;
1538 if (m_nConflicts==0)
1540 // When the last conflict is resolved we remove
1541 // the warning which we assume is in the last line.
1542 int nIndex = GetItemCount()-1;
1543 VERIFY(DeleteItem(nIndex));
1545 delete m_arData[nIndex];
1546 m_arData.pop_back();
1548 sResolvedPaths += data->path.GetWinPathString() + _T("\n");
1553 Invalidate();
1554 CString info = BuildInfoString();
1555 SetDlgItemText(IDC_INFOTEXT, info);
1557 if (!sResolvedPaths.IsEmpty())
1559 CString msg;
1560 msg.Format(IDS_SVNPROGRESS_RESOLVED, (LPCTSTR)sResolvedPaths);
1561 CMessageBox::Show(m_hWnd, msg, _T("TortoiseGit"), MB_OK | MB_ICONINFORMATION);
1564 break;
1565 #endif
1566 case ID_LOG:
1568 CString cmd = _T("/command:log");
1569 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1570 if(data->action == git_wc_notify_update_ref)
1572 cmd += _T(" /path:\"") + GetPathFromColumnText(CString()) + _T("\"");
1573 if (!data->m_OldHash.IsEmpty())
1574 cmd += _T(" /startrev:") + data->m_OldHash.ToString();
1575 if (!data->m_NewHash.IsEmpty())
1576 cmd += _T(" /endrev:") + data->m_NewHash.ToString();
1578 else
1579 cmd += _T(" /path:\"") + sPath + _T("\"");
1580 CAppUtils::RunTortoiseGitProc(cmd);
1582 break;
1583 case ID_OPENWITH:
1584 bOpenWith = true;
1585 case ID_OPEN:
1587 int ret = 0;
1588 CString sWinPath = GetPathFromColumnText(data->sPathColumnText);
1589 if (!bOpenWith)
1590 ret = (int)ShellExecute(this->m_hWnd, NULL, (LPCTSTR)sWinPath, NULL, NULL, SW_SHOWNORMAL);
1591 if ((ret <= HINSTANCE_ERROR)||bOpenWith)
1593 CString cmd = _T("RUNDLL32 Shell32,OpenAs_RunDLL ");
1594 cmd += sWinPath + _T(" ");
1595 CAppUtils::LaunchApplication(cmd, NULL, false);
1599 #if 0 //need
1600 DialogEnableWindow(IDOK, TRUE);
1601 #endif
1602 theApp.DoWaitCursor(-1);
1603 } // if (bAdded)
1609 void CGitProgressList::OnLvnBegindragSvnprogress(NMHDR* , LRESULT *pResult)
1611 //LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
1612 #if 0
1613 int selIndex = GetSelectionMark();
1614 if (selIndex < 0)
1615 return;
1617 CDropFiles dropFiles; // class for creating DROPFILES struct
1619 int index;
1620 POSITION pos = GetFirstSelectedItemPosition();
1621 while ( (index = GetNextSelectedItem(pos)) >= 0 )
1623 NotificationData * data = m_arData[index];
1625 if ( data->kind==svn_node_file || data->kind==svn_node_dir )
1627 CString sPath = GetPathFromColumnText(data->sPathColumnText);
1629 dropFiles.AddFile( sPath );
1633 if ( dropFiles.GetCount()>0 )
1635 dropFiles.CreateStructure();
1637 #endif
1638 *pResult = 0;
1641 void CGitProgressList::OnSize(UINT nType, int cx, int cy)
1643 CListCtrl::OnSize(nType, cx, cy);
1644 if ((nType == SIZE_RESTORED)&&(m_bLastVisible))
1646 if(!m_hWnd)
1647 return;
1649 int count = GetItemCount();
1650 if (count > 0)
1651 EnsureVisible(count-1, false);
1655 //////////////////////////////////////////////////////////////////////////
1656 /// commands
1657 //////////////////////////////////////////////////////////////////////////
1658 bool CGitProgressList::CmdAdd(CString& sWindowTitle, bool& localoperation)
1660 localoperation = true;
1661 SetWindowTitle(IDS_PROGRS_TITLE_ADD, g_Git.m_CurrentDir + _T("\\") + m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
1662 SetBackgroundImage(IDI_ADD_BKG);
1663 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_ADD)));
1665 m_itemCountTotal = m_targetPathList.GetCount();
1667 if (CRegDWORD(_T("Software\\TortoiseGit\\UseLibgit2"), TRUE) == TRUE)
1669 git_repository *repo = NULL;
1670 git_index *index;
1672 CStringA gitdir = CUnicodeUtils::GetMulti(CTGitPath(g_Git.m_CurrentDir).GetGitPathString(), CP_UTF8);
1673 if (git_repository_open(&repo, gitdir.GetBuffer()))
1675 gitdir.ReleaseBuffer();
1676 ReportGitError();
1677 return false;
1679 gitdir.ReleaseBuffer();
1681 git_config * config;
1682 git_config_new(&config);
1684 CStringA projectConfigA = CUnicodeUtils::GetMulti(g_Git.GetGitLocalConfig(), CP_UTF8);
1685 if (git_config_add_file_ondisk(config, projectConfigA.GetBuffer(), 4, FALSE))
1687 projectConfigA.ReleaseBuffer();
1688 ReportGitError();
1689 git_config_free(config);
1690 git_repository_free(repo);
1691 return false;
1693 projectConfigA.ReleaseBuffer();
1694 CString globalConfig = g_Git.GetGitGlobalConfig();
1695 if (PathFileExists(globalConfig))
1697 CStringA globalConfigA = CUnicodeUtils::GetMulti(globalConfig, CP_UTF8);
1698 if (git_config_add_file_ondisk(config, globalConfigA.GetBuffer(), 3, FALSE))
1700 globalConfigA.ReleaseBuffer();
1701 ReportGitError();
1702 git_config_free(config);
1703 git_repository_free(repo);
1704 return false;
1706 globalConfigA.ReleaseBuffer();
1708 CString globalXDGConfig = g_Git.GetGitGlobalXDGConfig();
1709 if (PathFileExists(globalXDGConfig))
1711 CStringA globalXDGConfigA = CUnicodeUtils::GetMulti(globalXDGConfig, CP_UTF8);
1712 if (git_config_add_file_ondisk(config, globalXDGConfigA.GetBuffer(), 2, FALSE))
1714 globalXDGConfigA.ReleaseBuffer();
1715 ReportGitError();
1716 git_config_free(config);
1717 git_repository_free(repo);
1718 return false;
1720 globalXDGConfigA.ReleaseBuffer();
1722 CString systemConfig = g_Git.GetGitSystemConfig();
1723 if (!systemConfig.IsEmpty())
1725 CStringA systemConfigA = CUnicodeUtils::GetMulti(systemConfig, CP_UTF8);
1726 if (git_config_add_file_ondisk(config, systemConfigA.GetBuffer(), 1, FALSE))
1728 systemConfigA.ReleaseBuffer();
1729 ReportGitError();
1730 git_config_free(config);
1731 git_repository_free(repo);
1732 return false;
1734 systemConfigA.ReleaseBuffer();
1737 git_repository_set_config(repo, config);
1738 git_config_free(config);
1739 config = nullptr;
1741 if (git_repository_index(&index, repo))
1743 ReportGitError();
1744 git_repository_free(repo);
1745 return false;
1747 if (git_index_read(index))
1749 ReportGitError();
1750 git_index_free(index);
1751 git_repository_free(repo);
1752 return false;
1755 for (m_itemCount = 0; m_itemCount < m_itemCountTotal; ++m_itemCount)
1757 if (git_index_add_bypath(index, CStringA(CUnicodeUtils::GetMulti(m_targetPathList[m_itemCount].GetGitPathString(), CP_UTF8)).GetBuffer()))
1759 ReportGitError();
1760 git_index_free(index);
1761 git_repository_free(repo);
1762 return false;
1764 Notify(m_targetPathList[m_itemCount], git_wc_notify_add);
1766 if (IsCancelled() == TRUE)
1768 git_index_free(index);
1769 git_repository_free(repo);
1771 ReportUserCanceled();
1772 return false;
1776 if (git_index_write(index))
1778 ReportGitError();
1779 git_index_free(index);
1780 git_repository_free(repo);
1781 return false;
1784 git_index_free(index);
1785 git_repository_free(repo);
1787 else
1789 CMassiveGitTask mgt(L"add -f");
1790 if (!mgt.ExecuteWithNotify(&m_targetPathList, m_bCancelled, git_wc_notify_add, this))
1791 return false;
1794 CShellUpdater::Instance().AddPathsForUpdate(m_targetPathList);
1796 return true;
1799 bool CGitProgressList::CmdCopy(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1801 #if 0
1802 ASSERT(m_targetPathList.GetCount() == 1);
1803 sWindowTitle.LoadString(IDS_PROGRS_TITLE_COPY);
1804 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1805 SetBackgroundImage(IDI_COPY_BKG);
1807 CString sCmdInfo;
1808 sCmdInfo.Format(IDS_PROGRS_CMD_COPY,
1809 m_targetPathList[0].IsUrl() ? (LPCTSTR)m_targetPathList[0].GetSVNPathString() : m_targetPathList[0].GetWinPath(),
1810 (LPCTSTR)m_url.GetSVNPathString(), (LPCTSTR)m_Revision.ToString());
1811 ReportCmd(sCmdInfo);
1813 if (!Copy(m_targetPathList, m_url, m_Revision, m_pegRev, m_sMessage))
1815 ReportSVNError();
1816 return false;
1818 if (m_options & ProgOptSwitchAfterCopy)
1820 sCmdInfo.Format(IDS_PROGRS_CMD_SWITCH,
1821 m_targetPathList[0].GetWinPath(),
1822 (LPCTSTR)m_url.GetSVNPathString(), (LPCTSTR)m_Revision.ToString());
1823 ReportCmd(sCmdInfo);
1824 if (!Switch(m_targetPathList[0], m_url, SVNRev::REV_HEAD, SVNRev::REV_HEAD, m_depth, TRUE, m_options & ProgOptIgnoreExternals))
1826 if (!Switch(m_targetPathList[0], m_url, SVNRev::REV_HEAD, m_Revision, m_depth, TRUE, m_options & ProgOptIgnoreExternals))
1828 ReportSVNError();
1829 return false;
1833 else
1835 if (SVN::PathIsURL(m_url))
1837 CString sMsg(MAKEINTRESOURCE(IDS_PROGRS_COPY_WARNING));
1838 ReportNotification(sMsg);
1841 #endif
1842 return true;
1845 bool CGitProgressList::CmdExport(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1847 #if 0
1848 ASSERT(m_targetPathList.GetCount() == 1);
1849 sWindowTitle.LoadString(IDS_PROGRS_TITLE_EXPORT);
1850 sWindowTitle = m_url.GetUIFileOrDirectoryName()+_T(" - ")+sWindowTitle;
1851 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1852 SetBackgroundImage(IDI_EXPORT_BKG);
1853 CString eol;
1854 if (m_options & ProgOptEolCRLF)
1855 eol = _T("CRLF");
1856 if (m_options & ProgOptEolLF)
1857 eol = _T("LF");
1858 if (m_options & ProgOptEolCR)
1859 eol = _T("CR");
1860 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_EXPORT)));
1861 if (!Export(m_url, m_targetPathList[0], m_Revision, m_Revision, TRUE, m_options & ProgOptIgnoreExternals, m_depth, NULL, FALSE, eol))
1863 ReportSVNError();
1864 return false;
1866 #endif
1867 return true;
1870 bool CGitProgressList::CmdRename(CString& /*sWindowTitle*/, bool& /*localoperation*/)
1872 #if 0
1873 ASSERT(m_targetPathList.GetCount() == 1);
1874 if ((!m_targetPathList[0].IsUrl())&&(!m_url.IsUrl()))
1875 localoperation = true;
1876 sWindowTitle.LoadString(IDS_PROGRS_TITLE_RENAME);
1877 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
1878 SetBackgroundImage(IDI_RENAME_BKG);
1879 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RENAME)));
1880 if (!Move(m_targetPathList, m_url, m_Revision, m_sMessage))
1882 ReportSVNError();
1883 return false;
1885 #endif
1886 return true;
1889 bool CGitProgressList::CmdResolve(CString& sWindowTitle, bool& localoperation)
1892 localoperation = true;
1893 ASSERT(m_targetPathList.GetCount() == 1);
1894 SetWindowTitle(IDS_PROGRS_TITLE_RESOLVE, g_Git.m_CurrentDir + _T("\\") + m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
1895 SetBackgroundImage(IDI_RESOLVE_BKG);
1896 // check if the file may still have conflict markers in it.
1897 //BOOL bMarkers = FALSE;
1899 m_itemCountTotal = m_targetPathList.GetCount();
1900 for (m_itemCount = 0; m_itemCount < m_itemCountTotal; ++m_itemCount)
1902 CString cmd,out,tempmergefile;
1903 cmd.Format(_T("git.exe add -f -- \"%s\""),m_targetPathList[m_itemCount].GetGitPathString());
1904 if (g_Git.Run(cmd, &out, CP_UTF8))
1906 CMessageBox::Show(NULL,out,_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1907 m_bErrorsOccurred=true;
1908 return false;
1911 CAppUtils::RemoveTempMergeFile((CTGitPath &)m_targetPathList[m_itemCount]);
1913 Notify(m_targetPathList[m_itemCount], git_wc_notify_resolved);
1915 #if 0
1916 if ((m_options & ProgOptSkipConflictCheck) == 0)
1920 for (INT_PTR fileindex=0; (fileindex<m_targetPathList.GetCount()) && (bMarkers==FALSE); ++fileindex)
1922 if (!m_targetPathList[fileindex].IsDirectory())
1924 CStdioFile file(m_targetPathList[fileindex].GetWinPath(), CFile::typeBinary | CFile::modeRead);
1925 CString strLine = _T("");
1926 while (file.ReadString(strLine))
1928 if (strLine.Find(_T("<<<<<<<"))==0)
1930 bMarkers = TRUE;
1931 break;
1934 file.Close();
1938 catch (CFileException* pE)
1940 TRACE(_T("CFileException in Resolve!\n"));
1941 TCHAR error[10000] = {0};
1942 pE->GetErrorMessage(error, 10000);
1943 ReportError(error);
1944 pE->Delete();
1945 return false;
1948 if (bMarkers)
1950 if (CMessageBox::Show(m_hWnd, IDS_PROGRS_REVERTMARKERS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION)==IDYES)
1952 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RESOLVE)));
1953 for (INT_PTR fileindex=0; fileindex<m_targetPathList.GetCount(); ++fileindex)
1954 Resolve(m_targetPathList[fileindex], svn_wc_conflict_choose_merged, true);
1957 else
1959 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_RESOLVE)));
1960 for (INT_PTR fileindex=0; fileindex<m_targetPathList.GetCount(); ++fileindex)
1961 Resolve(m_targetPathList[fileindex], svn_wc_conflict_choose_merged, true);
1963 #endif
1964 CShellUpdater::Instance().AddPathsForUpdate(m_targetPathList);
1966 return true;
1969 bool CGitProgressList::CmdRevert(CString& sWindowTitle, bool& localoperation)
1972 localoperation = true;
1973 SetWindowTitle(IDS_PROGRS_TITLE_REVERT, g_Git.m_CurrentDir + _T("\\") + m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
1974 SetBackgroundImage(IDI_REVERT_BKG);
1976 m_itemCountTotal = 2 * m_selectedPaths.GetCount();
1977 CTGitPathList delList;
1978 for (m_itemCount = 0; m_itemCount < m_selectedPaths.GetCount(); ++m_itemCount)
1980 CTGitPath path;
1981 int action;
1982 path.SetFromWin(g_Git.m_CurrentDir + _T("\\") + m_selectedPaths[m_itemCount].GetWinPath());
1983 action = m_selectedPaths[m_itemCount].m_Action;
1984 /* rename file can't delete because it needs original file*/
1985 if((!(action & CTGitPath::LOGACTIONS_ADDED)) &&
1986 (!(action & CTGitPath::LOGACTIONS_REPLACED)))
1987 delList.AddPath(path);
1989 if (DWORD(CRegDWORD(_T("Software\\TortoiseGit\\RevertWithRecycleBin"), TRUE)))
1990 delList.DeleteAllFiles(true);
1992 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_REVERT)));
1993 for (int i = 0; i < m_selectedPaths.GetCount(); ++i)
1995 if(g_Git.Revert(_T("HEAD"), (CTGitPath&)m_selectedPaths[i]))
1997 CMessageBox::Show(NULL,_T("Revert Fail"),_T("TortoiseGit"),MB_OK|MB_ICONERROR);
1998 m_bErrorsOccurred=true;
1999 return false;
2001 Notify(m_selectedPaths[i], git_wc_notify_revert);
2002 ++m_itemCount;
2005 CShellUpdater::Instance().AddPathsForUpdate(m_selectedPaths);
2007 return true;
2010 bool CGitProgressList::CmdSwitch(CString& /*sWindowTitle*/, bool& /*localoperation*/)
2012 #if 0
2013 ASSERT(m_targetPathList.GetCount() == 1);
2014 SVNStatus st;
2015 sWindowTitle.LoadString(IDS_PROGRS_TITLE_SWITCH);
2016 SetWindowText(sWindowTitle); // needs to be updated, see TSVN rev. 21375
2017 SetBackgroundImage(IDI_SWITCH_BKG);
2018 LONG rev = 0;
2019 if (st.GetStatus(m_targetPathList[0]) != (-2))
2021 if (st.status->entry != NULL)
2023 rev = st.status->entry->revision;
2027 CString sCmdInfo;
2028 sCmdInfo.Format(IDS_PROGRS_CMD_SWITCH,
2029 m_targetPathList[0].GetWinPath(), (LPCTSTR)m_url.GetSVNPathString(),
2030 (LPCTSTR)m_Revision.ToString());
2031 ReportCmd(sCmdInfo);
2033 bool depthIsSticky = true;
2034 if (m_depth == svn_depth_unknown)
2035 depthIsSticky = false;
2036 if (!Switch(m_targetPathList[0], m_url, m_Revision, m_Revision, m_depth, depthIsSticky, m_options & ProgOptIgnoreExternals))
2038 ReportSVNError();
2039 return false;
2041 m_UpdateStartRevMap[m_targetPathList[0].GetSVNApiPath(pool)] = rev;
2042 if ((m_RevisionEnd >= 0)&&(rev >= 0)
2043 &&((LONG)m_RevisionEnd > (LONG)rev))
2045 GetDlgItem(IDC_LOGBUTTON)->ShowWindow(SW_SHOW);
2047 #endif
2048 return true;
2050 bool CGitProgressList::CmdClone(CString& sWindowTitle, bool& /*localoperation*/)
2052 if (!g_Git.UsingLibGit2(CGit::GIT_CMD_CLONE))
2054 // should never run to here
2055 ASSERT(FALSE);
2056 return false;
2058 this->m_TotalBytesTransferred = 0;
2060 SetWindowTitle(IDS_PROGRS_TITLE_CLONE, m_url.GetGitPathString(), sWindowTitle);
2061 SetBackgroundImage(IDI_SWITCH_BKG);
2062 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROG_CLONE)));
2064 if (m_url.IsEmpty() || m_targetPathList.GetCount() == 0)
2065 return false;
2067 CStringA url = CUnicodeUtils::GetMulti(m_url.GetGitPathString(), CP_UTF8);
2068 CStringA path = CUnicodeUtils::GetMulti(m_targetPathList[0].GetWinPathString(),CP_UTF8);
2070 git_repository *cloned_repo = NULL;
2071 git_remote *origin = NULL;
2072 git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
2074 int error = 0;
2076 git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
2078 clone_opts.checkout_opts = checkout_opts;
2080 clone_opts.checkout_opts.checkout_strategy = m_bNoCheckout? GIT_CHECKOUT_NONE : GIT_CHECKOUT_SAFE_CREATE;
2081 clone_opts.checkout_opts.progress_cb = CheckoutCallback;
2082 clone_opts.checkout_opts.progress_payload = this;
2084 git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
2086 callbacks.update_tips = RemoteUpdatetipsCallback;
2087 callbacks.progress = RemoteProgressCallback;
2088 callbacks.completion = RemoteCompletionCallback;
2089 callbacks.payload = this;
2091 clone_opts.remote_callbacks = &callbacks;
2093 if (!m_RefSpec.IsEmpty())
2094 clone_opts.checkout_branch = CUnicodeUtils::GetMulti(m_RefSpec, CP_UTF8).GetBuffer();
2096 clone_opts.fetch_progress_cb = FetchCallback;
2097 clone_opts.fetch_progress_payload = this;
2099 clone_opts.cred_acquire_cb = CAppUtils::Git2GetUserPassword;
2101 clone_opts.bare = m_bBare;
2103 if(m_pAnimate)
2105 m_pAnimate->ShowWindow(SW_SHOW);
2106 m_pAnimate->Play(0, INT_MAX, INT_MAX);
2108 error = git_clone(&cloned_repo, url, path, &clone_opts);
2110 if (m_pAnimate)
2112 m_pAnimate->Stop();
2113 m_pAnimate->ShowWindow(SW_HIDE);
2116 git_remote_free(origin);
2117 if (error)
2119 ReportGitError();
2120 return false;
2122 else if (cloned_repo)
2123 git_repository_free(cloned_repo);
2124 return true;
2126 bool CGitProgressList::CmdSendMail(CString& sWindowTitle, bool& /*localoperation*/)
2128 ASSERT(m_SendMail);
2129 SetWindowTitle(IDS_PROGRS_TITLE_SENDMAIL, g_Git.m_CurrentDir + _T("\\") + m_targetPathList.GetCommonRoot().GetUIPathString(), sWindowTitle);
2130 //SetBackgroundImage(IDI_ADD_BKG);
2131 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_CMD_SENDMAIL)));
2133 return m_SendMail->Send(m_targetPathList, this) == 0;
2136 bool CGitProgressList::CmdFetch(CString& sWindowTitle, bool& /*localoperation*/)
2138 if (!g_Git.UsingLibGit2(CGit::GIT_CMD_FETCH))
2140 // should never run to here
2141 ASSERT(0);
2142 return false;
2144 this->m_TotalBytesTransferred = 0;
2146 SetWindowTitle(IDS_PROGRS_TITLE_FETCH, g_Git.m_CurrentDir, sWindowTitle);
2147 SetBackgroundImage(IDI_UPDATE_BKG);
2148 ReportCmd(CString(MAKEINTRESOURCE(IDS_PROGRS_TITLE_FETCH)) + _T(" ") + m_url.GetGitPathString() + _T(" ") + m_RefSpec);
2150 CStringA url = CUnicodeUtils::GetMulti(m_url.GetGitPathString(), CP_UTF8);
2151 CStringA gitdir = CUnicodeUtils::GetMulti(CTGitPath(g_Git.m_CurrentDir).GetGitPathString(), CP_UTF8);
2152 CStringA remotebranch = CUnicodeUtils::GetMulti(m_RefSpec, CP_UTF8);
2154 git_remote *remote = NULL;
2155 git_repository *repo = NULL;
2156 bool ret = true;
2160 if (m_pAnimate)
2162 m_pAnimate->ShowWindow(SW_SHOW);
2163 m_pAnimate->Play(0, INT_MAX, INT_MAX);
2166 if (git_repository_open(&repo, gitdir.GetBuffer()))
2168 gitdir.ReleaseBuffer();
2169 ReportGitError();
2170 ret = false;
2171 break;
2174 // first try with a named remote (e.g. "origin")
2175 if (git_remote_load(&remote, repo, url) < 0)
2177 // retry with repository located at a specific url
2178 if (git_remote_create_inmemory(&remote, repo, NULL, url) < 0)
2180 ReportGitError();
2181 ret = false;
2182 break;
2186 git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
2188 callbacks.update_tips = RemoteUpdatetipsCallback;
2189 callbacks.progress = RemoteProgressCallback;
2190 callbacks.completion = RemoteCompletionCallback;
2191 callbacks.payload = this;
2193 git_remote_set_callbacks(remote, &callbacks);
2194 git_remote_set_cred_acquire_cb(remote, CAppUtils::Git2GetUserPassword, NULL);
2195 git_remote_set_autotag(remote, (git_remote_autotag_option_t)m_AutoTag);
2197 if (!remotebranch.IsEmpty() && git_remote_set_fetchspec(remote, remotebranch))
2199 ReportGitError();
2200 ret = false;
2201 break;
2204 // Connect to the remote end specifying that we want to fetch
2205 // information from it.
2206 if (git_remote_connect(remote, GIT_DIRECTION_FETCH) < 0) {
2207 ReportGitError();
2208 ret = false;
2209 break;
2212 // Download the packfile and index it. This function updates the
2213 // amount of received data and the indexer stats which lets you
2214 // inform the user about progress.
2215 if (git_remote_download(remote, FetchCallback, this) < 0) {
2216 ReportGitError();
2217 ret = false;
2218 break;
2221 if (m_pAnimate)
2223 m_pAnimate->ShowWindow(SW_HIDE);
2225 // Update the references in the remote's namespace to point to the
2226 // right commits. This may be needed even if there was no packfile
2227 // to download, which can happen e.g. when the branches have been
2228 // changed but all the neede objects are available locally.
2229 if (git_remote_update_tips(remote) < 0)
2231 ReportGitError();
2232 ret = false;
2233 break;
2236 git_remote_disconnect(remote);
2238 } while(0);
2240 git_remote_free(remote);
2241 git_repository_free(repo);
2242 if (m_pAnimate)
2243 m_pAnimate->ShowWindow(SW_HIDE);
2244 return ret;
2248 void CGitProgressList::Init()
2250 SetExtendedStyle (LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER);
2252 DeleteAllItems();
2253 int c = ((CHeaderCtrl*)(GetDlgItem(0)))->GetItemCount()-1;
2254 while (c>=0)
2255 DeleteColumn(c--);
2257 CString temp;
2258 temp.LoadString(IDS_PROGRS_ACTION);
2259 InsertColumn(0, temp);
2260 temp.LoadString(IDS_PROGRS_PATH);
2261 InsertColumn(1, temp);
2263 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
2264 if (m_pThread==NULL)
2266 ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
2268 else
2270 m_pThread->m_bAutoDelete = FALSE;
2271 m_pThread->ResumeThread();
2274 // Call this early so that the column headings aren't hidden before any
2275 // text gets added.
2276 ResizeColumns();
2278 SetTimer(VISIBLETIMER, 300, NULL);
2282 void CGitProgressList::OnClose()
2284 // TODO: Add your message handler code here and/or call default
2285 if (m_bCancelled)
2287 TerminateThread(m_pThread->m_hThread, (DWORD)-1);
2288 InterlockedExchange(&m_bThreadRunning, FALSE);
2290 else
2292 m_bCancelled = TRUE;
2293 return;
2295 CListCtrl::OnClose();
2299 BOOL CGitProgressList::PreTranslateMessage(MSG* pMsg)
2301 // TODO: Add your specialized code here and/or call the base class
2302 if (pMsg->message == WM_KEYDOWN)
2304 if (pMsg->wParam == 'A')
2306 if (GetKeyState(VK_CONTROL)&0x8000)
2308 // Ctrl-A -> select all
2309 SetSelectionMark(0);
2310 for (int i=0; i<GetItemCount(); ++i)
2312 SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
2316 if ((pMsg->wParam == 'C')||(pMsg->wParam == VK_INSERT))
2318 int selIndex = GetSelectionMark();
2319 if (selIndex >= 0)
2321 if (GetKeyState(VK_CONTROL)&0x8000)
2323 //Ctrl-C -> copy to clipboard
2324 CString sClipdata;
2325 POSITION pos = GetFirstSelectedItemPosition();
2326 if (pos != NULL)
2328 while (pos)
2330 int nItem = GetNextSelectedItem(pos);
2331 CString sAction = GetItemText(nItem, 0);
2332 CString sPath = GetItemText(nItem, 1);
2333 CString sMime = GetItemText(nItem, 2);
2334 CString sLogCopyText;
2335 sLogCopyText.Format(_T("%s: %s %s\r\n"),
2336 (LPCTSTR)sAction, (LPCTSTR)sPath, (LPCTSTR)sMime);
2337 sClipdata += sLogCopyText;
2339 CStringUtils::WriteAsciiStringToClipboard(sClipdata);
2344 } // if (pMsg->message == WM_KEYDOWN)
2345 return CListCtrl::PreTranslateMessage(pMsg);
2348 CString CGitProgressList::GetPathFromColumnText(const CString& sColumnText)
2350 CString sPath = sColumnText;
2351 if (sPath.Find(':')<0)
2353 // the path is not absolute: add the common root of all paths to it
2354 sPath = g_Git.m_CurrentDir + _T("\\") + sColumnText;
2356 return sPath;
2359 void CGitProgressList::SetWindowTitle(UINT id, const CString& urlorpath, CString& dialogname)
2361 if (!m_bSetTitle || !m_pPostWnd)
2362 return;
2364 dialogname.LoadString(id);
2365 CAppUtils::SetWindowTitle(m_pPostWnd->GetSafeHwnd(), urlorpath, dialogname);