OSDN Git Service

status bar cancel button work
[tortoisegit/TortoiseGitJp.git] / TortoiseProc / CommitDlg.cpp
1 // TortoiseGit - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseGit\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 #include "stdafx.h"\r
20 #include "TortoiseProc.h"\r
21 #include "CommitDlg.h"\r
22 #include "DirFileEnum.h"\r
23 //#include "GitConfig.h"\r
24 //#include "GitProperties.h"\r
25 #include "MessageBox.h"\r
26 #include "AppUtils.h"\r
27 #include "PathUtils.h"\r
28 #include "Git.h"\r
29 #include "Registry.h"\r
30 #include "GitStatus.h"\r
31 #include "HistoryDlg.h"\r
32 #include "Hooks.h"\r
33 \r
34 #ifdef _DEBUG\r
35 #define new DEBUG_NEW\r
36 #undef THIS_FILE\r
37 static char THIS_FILE[] = __FILE__;\r
38 #endif\r
39 \r
40 UINT CCommitDlg::WM_AUTOLISTREADY = RegisterWindowMessage(_T("TORTOISEGIT_AUTOLISTREADY_MSG"));\r
41 \r
42 IMPLEMENT_DYNAMIC(CCommitDlg, CResizableStandAloneDialog)\r
43 CCommitDlg::CCommitDlg(CWnd* pParent /*=NULL*/)\r
44         : CResizableStandAloneDialog(CCommitDlg::IDD, pParent)\r
45         , m_bRecursive(FALSE)\r
46         , m_bShowUnversioned(FALSE)\r
47         , m_bBlock(FALSE)\r
48         , m_bThreadRunning(FALSE)\r
49         , m_bRunThread(FALSE)\r
50         , m_pThread(NULL)\r
51         , m_bKeepLocks(FALSE)\r
52         , m_bKeepChangeList(TRUE)\r
53         , m_itemsCount(0)\r
54         , m_bSelectFilesForCommit(TRUE)\r
55 {\r
56 }\r
57 \r
58 CCommitDlg::~CCommitDlg()\r
59 {\r
60         if(m_pThread != NULL)\r
61         {\r
62                 delete m_pThread;\r
63         }\r
64 }\r
65 \r
66 void CCommitDlg::DoDataExchange(CDataExchange* pDX)\r
67 {\r
68         CResizableStandAloneDialog::DoDataExchange(pDX);\r
69         DDX_Control(pDX, IDC_FILELIST, m_ListCtrl);\r
70         DDX_Control(pDX, IDC_LOGMESSAGE, m_cLogMessage);\r
71         DDX_Check(pDX, IDC_SHOWUNVERSIONED, m_bShowUnversioned);\r
72         DDX_Control(pDX, IDC_SELECTALL, m_SelectAll);\r
73         DDX_Text(pDX, IDC_BUGID, m_sBugID);\r
74         DDX_Check(pDX, IDC_KEEPLOCK, m_bKeepLocks);\r
75         DDX_Control(pDX, IDC_SPLITTER, m_wndSplitter);\r
76         DDX_Check(pDX, IDC_KEEPLISTS, m_bKeepChangeList);\r
77 }\r
78 \r
79 BEGIN_MESSAGE_MAP(CCommitDlg, CResizableStandAloneDialog)\r
80         ON_BN_CLICKED(IDC_SELECTALL, OnBnClickedSelectall)\r
81         ON_BN_CLICKED(IDHELP, OnBnClickedHelp)\r
82         ON_BN_CLICKED(IDC_SHOWUNVERSIONED, OnBnClickedShowunversioned)\r
83 //      ON_BN_CLICKED(IDC_HISTORY, OnBnClickedHistory)\r
84         ON_BN_CLICKED(IDC_BUGTRAQBUTTON, OnBnClickedBugtraqbutton)\r
85         ON_EN_CHANGE(IDC_LOGMESSAGE, OnEnChangeLogmessage)\r
86 //      ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_ITEMCOUNTCHANGED, OnGitStatusListCtrlItemCountChanged)\r
87 //      ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_NEEDSREFRESH, OnGitStatusListCtrlNeedsRefresh)\r
88 //      ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_ADDFILE, OnFileDropped)\r
89 //      ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_CHECKCHANGED, &CCommitDlg::OnGitStatusListCtrlCheckChanged)\r
90         ON_REGISTERED_MESSAGE(WM_AUTOLISTREADY, OnAutoListReady) \r
91         ON_WM_TIMER()\r
92     ON_WM_SIZE()\r
93         ON_STN_CLICKED(IDC_EXTERNALWARNING, &CCommitDlg::OnStnClickedExternalwarning)\r
94         ON_BN_CLICKED(IDC_SIGN_OFF, &CCommitDlg::OnBnClickedSignOff)\r
95 END_MESSAGE_MAP()\r
96 \r
97 BOOL CCommitDlg::OnInitDialog()\r
98 {\r
99         CResizableStandAloneDialog::OnInitDialog();\r
100         \r
101         m_regAddBeforeCommit = CRegDWORD(_T("Software\\TortoiseGit\\AddBeforeCommit"), TRUE);\r
102         m_bShowUnversioned = m_regAddBeforeCommit;\r
103 \r
104         m_History.SetMaxHistoryItems((LONG)CRegDWORD(_T("Software\\TortoiseGit\\MaxHistoryItems"), 25));\r
105 \r
106         m_regKeepChangelists = CRegDWORD(_T("Software\\TortoiseGit\\KeepChangeLists"), FALSE);\r
107         m_bKeepChangeList = m_regKeepChangelists;\r
108 \r
109 //      GitConfig config;\r
110 //      m_bKeepLocks = config.KeepLocks();\r
111 \r
112         UpdateData(FALSE);\r
113         \r
114         m_ListCtrl.Init(SVNSLC_COLEXT | SVNSLC_COLTEXTSTATUS , _T("CommitDlg"));\r
115         m_ListCtrl.SetSelectButton(&m_SelectAll);\r
116         m_ListCtrl.SetStatLabel(GetDlgItem(IDC_STATISTICS));\r
117         m_ListCtrl.SetCancelBool(&m_bCancelled);\r
118         m_ListCtrl.SetEmptyString(IDS_COMMITDLG_NOTHINGTOCOMMIT);\r
119         m_ListCtrl.EnableFileDrop();\r
120         m_ListCtrl.SetBackgroundImage(IDI_COMMIT_BKG);\r
121         \r
122 //      m_ProjectProperties.ReadPropsPathList(m_pathList);\r
123         m_cLogMessage.Init(m_ProjectProperties);\r
124         m_cLogMessage.SetFont((CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));\r
125         m_cLogMessage.RegisterContextMenuHandler(this);\r
126 \r
127         OnEnChangeLogmessage();\r
128 \r
129         m_tooltips.Create(this);\r
130         m_tooltips.AddTool(IDC_EXTERNALWARNING, IDS_COMMITDLG_EXTERNALS);\r
131 //      m_tooltips.AddTool(IDC_HISTORY, IDS_COMMITDLG_HISTORY_TT);\r
132         \r
133         m_SelectAll.SetCheck(BST_INDETERMINATE);\r
134 \r
135 #if 0\r
136         CBugTraqAssociations bugtraq_associations;\r
137         bugtraq_associations.Load();\r
138 \r
139         if (bugtraq_associations.FindProvider(m_pathList, &m_bugtraq_association))\r
140         {\r
141                 GetDlgItem(IDC_BUGID)->ShowWindow(SW_HIDE);\r
142                 GetDlgItem(IDC_BUGIDLABEL)->ShowWindow(SW_HIDE);\r
143 \r
144                 CComPtr<IBugTraqProvider> pProvider;\r
145                 HRESULT hr = pProvider.CoCreateInstance(m_bugtraq_association.GetProviderClass());\r
146                 if (SUCCEEDED(hr))\r
147                 {\r
148                         m_BugTraqProvider = pProvider;\r
149                         BSTR temp = NULL;\r
150                         if (SUCCEEDED(hr = pProvider->GetLinkText(GetSafeHwnd(), m_bugtraq_association.GetParameters().AllocSysString(), &temp)))\r
151                         {\r
152                                 SetDlgItemText(IDC_BUGTRAQBUTTON, temp);\r
153                                 GetDlgItem(IDC_BUGTRAQBUTTON)->EnableWindow(TRUE);\r
154                                 GetDlgItem(IDC_BUGTRAQBUTTON)->ShowWindow(SW_SHOW);\r
155                         }\r
156 \r
157                         SysFreeString(temp);\r
158                 }\r
159 \r
160                 GetDlgItem(IDC_LOGMESSAGE)->SetFocus();\r
161         }\r
162         else if (!m_ProjectProperties.sMessage.IsEmpty())\r
163         {\r
164                 GetDlgItem(IDC_BUGID)->ShowWindow(SW_SHOW);\r
165                 GetDlgItem(IDC_BUGIDLABEL)->ShowWindow(SW_SHOW);\r
166                 if (!m_ProjectProperties.sLabel.IsEmpty())\r
167                         SetDlgItemText(IDC_BUGIDLABEL, m_ProjectProperties.sLabel);\r
168                 GetDlgItem(IDC_BUGTRAQBUTTON)->ShowWindow(SW_HIDE);\r
169                 GetDlgItem(IDC_BUGTRAQBUTTON)->EnableWindow(FALSE);\r
170                 GetDlgItem(IDC_BUGID)->SetFocus();\r
171                 CString sBugID = m_ProjectProperties.GetBugIDFromLog(m_sLogMessage);\r
172                 if (!sBugID.IsEmpty())\r
173                 {\r
174                         SetDlgItemText(IDC_BUGID, sBugID);\r
175                 }\r
176         }\r
177         else\r
178         {\r
179                 GetDlgItem(IDC_BUGID)->ShowWindow(SW_HIDE);\r
180                 GetDlgItem(IDC_BUGIDLABEL)->ShowWindow(SW_HIDE);\r
181                 GetDlgItem(IDC_BUGTRAQBUTTON)->ShowWindow(SW_HIDE);\r
182                 GetDlgItem(IDC_BUGTRAQBUTTON)->EnableWindow(FALSE);\r
183                 GetDlgItem(IDC_LOGMESSAGE)->SetFocus();\r
184         }\r
185 #endif\r
186         if (!m_sLogMessage.IsEmpty())\r
187                 m_cLogMessage.SetText(m_sLogMessage);\r
188                 \r
189         GetWindowText(m_sWindowTitle);\r
190         \r
191         AdjustControlSize(IDC_SHOWUNVERSIONED);\r
192         AdjustControlSize(IDC_SELECTALL);\r
193         AdjustControlSize(IDC_KEEPLOCK);\r
194 \r
195         GetClientRect(m_DlgOrigRect);\r
196         m_cLogMessage.GetClientRect(m_LogMsgOrigRect);\r
197 \r
198         AddAnchor(IDC_COMMITLABEL, TOP_LEFT, TOP_RIGHT);\r
199         AddAnchor(IDC_BUGIDLABEL, TOP_RIGHT);\r
200         AddAnchor(IDC_BUGID, TOP_RIGHT);\r
201         AddAnchor(IDC_BUGTRAQBUTTON, TOP_RIGHT);\r
202         AddAnchor(IDC_COMMIT_TO, TOP_LEFT, TOP_RIGHT);\r
203         AddAnchor(IDC_MESSAGEGROUP, TOP_LEFT, TOP_RIGHT);\r
204 //      AddAnchor(IDC_HISTORY, TOP_LEFT);\r
205         AddAnchor(IDC_LOGMESSAGE, TOP_LEFT, TOP_RIGHT);\r
206         \r
207         AddAnchor(IDC_LISTGROUP, TOP_LEFT, BOTTOM_RIGHT);\r
208         AddAnchor(IDC_SPLITTER, TOP_LEFT, TOP_RIGHT);\r
209         AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);\r
210         AddAnchor(IDC_SHOWUNVERSIONED, BOTTOM_LEFT);\r
211         AddAnchor(IDC_SELECTALL, BOTTOM_LEFT);\r
212         AddAnchor(IDC_EXTERNALWARNING, BOTTOM_RIGHT);\r
213         AddAnchor(IDC_STATISTICS, BOTTOM_LEFT, BOTTOM_RIGHT);\r
214         AddAnchor(IDC_KEEPLOCK, BOTTOM_LEFT);\r
215         AddAnchor(IDC_KEEPLISTS, BOTTOM_LEFT);\r
216         AddAnchor(IDOK, BOTTOM_RIGHT);\r
217         AddAnchor(IDCANCEL, BOTTOM_RIGHT);\r
218         AddAnchor(IDHELP, BOTTOM_RIGHT);\r
219         if (hWndExplorer)\r
220                 CenterWindow(CWnd::FromHandle(hWndExplorer));\r
221         EnableSaveRestore(_T("CommitDlg"));\r
222         DWORD yPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\CommitDlgSizer"));\r
223         RECT rcDlg, rcLogMsg, rcFileList;\r
224         GetClientRect(&rcDlg);\r
225         m_cLogMessage.GetWindowRect(&rcLogMsg);\r
226         ScreenToClient(&rcLogMsg);\r
227         m_ListCtrl.GetWindowRect(&rcFileList);\r
228         ScreenToClient(&rcFileList);\r
229         if (yPos)\r
230         {\r
231                 RECT rectSplitter;\r
232                 m_wndSplitter.GetWindowRect(&rectSplitter);\r
233                 ScreenToClient(&rectSplitter);\r
234                 int delta = yPos - rectSplitter.top;\r
235                 if ((rcLogMsg.bottom + delta > rcLogMsg.top)&&(rcLogMsg.bottom + delta < rcFileList.bottom - 30))\r
236                 {\r
237                         m_wndSplitter.SetWindowPos(NULL, 0, yPos, 0, 0, SWP_NOSIZE);\r
238                         DoSize(delta);\r
239                 }\r
240         }\r
241 \r
242         // add all directories to the watcher\r
243         for (int i=0; i<m_pathList.GetCount(); ++i)\r
244         {\r
245                 if (m_pathList[i].IsDirectory())\r
246                         m_pathwatcher.AddPath(m_pathList[i]);\r
247         }\r
248 \r
249         m_updatedPathList = m_pathList;\r
250 \r
251         //first start a thread to obtain the file list with the status without\r
252         //blocking the dialog\r
253         InterlockedExchange(&m_bBlock, TRUE);\r
254         m_pThread = AfxBeginThread(StatusThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);\r
255         if (m_pThread==NULL)\r
256         {\r
257                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
258                 InterlockedExchange(&m_bBlock, FALSE);\r
259         }\r
260         else\r
261         {\r
262                 m_pThread->m_bAutoDelete = FALSE;\r
263                 m_pThread->ResumeThread();\r
264         }\r
265         CRegDWORD err = CRegDWORD(_T("Software\\TortoiseGit\\ErrorOccurred"), FALSE);\r
266         CRegDWORD historyhint = CRegDWORD(_T("Software\\TortoiseGit\\HistoryHintShown"), FALSE);\r
267         if ((((DWORD)err)!=FALSE)&&((((DWORD)historyhint)==FALSE)))\r
268         {\r
269                 historyhint = TRUE;\r
270 //              ShowBalloon(IDC_HISTORY, IDS_COMMITDLG_HISTORYHINT_TT, IDI_INFORMATION);\r
271         }\r
272         err = FALSE;\r
273 \r
274 \r
275         return FALSE;  // return TRUE unless you set the focus to a control\r
276         // EXCEPTION: OCX Property Pages should return FALSE\r
277 }\r
278 \r
279 void CCommitDlg::OnOK()\r
280 {\r
281         if (m_bBlock)\r
282                 return;\r
283         if (m_bThreadRunning)\r
284         {\r
285                 m_bCancelled = true;\r
286                 InterlockedExchange(&m_bRunThread, FALSE);\r
287                 WaitForSingleObject(m_pThread->m_hThread, 1000);\r
288                 if (m_bThreadRunning)\r
289                 {\r
290                         // we gave the thread a chance to quit. Since the thread didn't\r
291                         // listen to us we have to kill it.\r
292                         TerminateThread(m_pThread->m_hThread, (DWORD)-1);\r
293                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
294                 }\r
295         }\r
296         CString id;\r
297         GetDlgItemText(IDC_BUGID, id);\r
298         if (!m_ProjectProperties.CheckBugID(id))\r
299         {\r
300                 ShowBalloon(IDC_BUGID, IDS_COMMITDLG_ONLYNUMBERS, IDI_EXCLAMATION);\r
301                 return;\r
302         }\r
303         m_sLogMessage = m_cLogMessage.GetText();\r
304         if ((m_ProjectProperties.bWarnIfNoIssue) && (id.IsEmpty() && !m_ProjectProperties.HasBugID(m_sLogMessage)))\r
305         {\r
306                 if (CMessageBox::Show(this->m_hWnd, IDS_COMMITDLG_NOISSUEWARNING, IDS_APPNAME, MB_YESNO | MB_ICONWARNING)!=IDYES)\r
307                         return;\r
308         }\r
309 \r
310 #if 0\r
311         CRegDWORD regUnversionedRecurse (_T("Software\\TortoiseGit\\UnversionedRecurse"), TRUE);\r
312         if (!(DWORD)regUnversionedRecurse)\r
313         {\r
314                 // Find unversioned directories which are marked for commit. The user might expect them\r
315                 // to be added recursively since he cannot the the files. Let's ask the user if he knows\r
316                 // what he is doing.\r
317                 int nListItems = m_ListCtrl.GetItemCount();\r
318                 for (int j=0; j<nListItems; j++)\r
319                 {\r
320                         const CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(j);\r
321                         if (entry->IsChecked() && (entry->status == Git_wc_status_unversioned) && entry->IsFolder() )\r
322                         {\r
323                                 if (CMessageBox::Show(this->m_hWnd, IDS_COMMITDLG_UNVERSIONEDFOLDERWARNING, IDS_APPNAME, MB_YESNO | MB_ICONWARNING)!=IDYES)\r
324                                         return;\r
325                         }\r
326                 }\r
327         }\r
328 #endif\r
329         m_pathwatcher.Stop();\r
330         InterlockedExchange(&m_bBlock, TRUE);\r
331         CDWordArray arDeleted;\r
332         //first add all the unversioned files the user selected\r
333         //and check if all versioned files are selected\r
334         int nUnchecked = 0;\r
335         m_bRecursive = true;\r
336         int nListItems = m_ListCtrl.GetItemCount();\r
337 \r
338         CTGitPathList itemsToAdd;\r
339         CTGitPathList itemsToRemove;\r
340         bool bCheckedInExternal = false;\r
341         bool bHasConflicted = false;\r
342         std::set<CString> checkedLists;\r
343         std::set<CString> uncheckedLists;\r
344 #if 0\r
345         for (int j=0; j<nListItems; j++)\r
346         {\r
347                 const CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(j);\r
348                 if (entry->IsChecked())\r
349                 {\r
350                         if (entry->status == Git_wc_status_unversioned)\r
351                         {\r
352                                 itemsToAdd.AddPath(entry->GetPath());\r
353                         }\r
354                         if (entry->status == Git_wc_status_conflicted)\r
355                         {\r
356                                 bHasConflicted = true;\r
357                         }\r
358                         if (entry->status == Git_wc_status_missing)\r
359                         {\r
360                                 itemsToRemove.AddPath(entry->GetPath());\r
361                         }\r
362                         if (entry->status == Git_wc_status_deleted)\r
363                         {\r
364                                 arDeleted.Add(j);\r
365                         }\r
366                         if (entry->IsInExternal())\r
367                         {\r
368                                 bCheckedInExternal = true;\r
369                         }\r
370                         checkedLists.insert(entry->GetChangeList());\r
371                 }\r
372                 else\r
373                 {\r
374                         if ((entry->status != Git_wc_status_unversioned)        &&\r
375                                 (entry->status != Git_wc_status_ignored))\r
376                         {\r
377                                 nUnchecked++;\r
378                                 uncheckedLists.insert(entry->GetChangeList());\r
379                                 if (m_bRecursive)\r
380                                 {\r
381                                         // This algorithm is for the sake of simplicity of the complexity O(N?\r
382                                         for (int k=0; k<nListItems; k++)\r
383                                         {\r
384                                                 const CGitStatusListCtrl::FileEntry * entryK = m_ListCtrl.GetListEntry(k);\r
385                                                 if (entryK->IsChecked() && entryK->GetPath().IsAncestorOf(entry->GetPath())  )\r
386                                                 {\r
387                                                         // Fall back to a non-recursive commit to prevent items being\r
388                                                         // committed which aren't checked although its parent is checked\r
389                                                         // (property change, directory deletion, ... )\r
390                                                         m_bRecursive = false;\r
391                                                         break;\r
392                                                 }\r
393                                         }\r
394                                 }\r
395                         }\r
396                 }\r
397         }\r
398 #endif\r
399 \r
400 #if 0\r
401         if (m_pathwatcher.GetNumberOfChangedPaths() && m_bRecursive)\r
402         {\r
403                 // There are paths which got changed (touched at least).\r
404                 // We have to find out if this affects the selection in the commit dialog\r
405                 // If it could affect the selection, revert back to a non-recursive commit\r
406                 CTGitPathList changedList = m_pathwatcher.GetChangedPaths();\r
407                 changedList.RemoveDuplicates();\r
408                 for (int i=0; i<changedList.GetCount(); ++i)\r
409                 {\r
410                         if (changedList[i].IsAdminDir())\r
411                         {\r
412                                 // something inside an admin dir was changed.\r
413                                 // if it's the entries file, then we have to fully refresh because\r
414                                 // files may have been added/removed from version control\r
415                                 if ((changedList[i].GetWinPathString().Right(7).CompareNoCase(_T("entries")) == 0) &&\r
416                                         (changedList[i].GetWinPathString().Find(_T("\\tmp\\"))<0))\r
417                                 {\r
418                                         m_bRecursive = false;\r
419                                         break;\r
420                                 }\r
421                         }\r
422                         else if (!m_ListCtrl.IsPathShown(changedList[i]))\r
423                         {\r
424                                 // a path which is not shown in the list has changed\r
425                                 CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(changedList[i]);\r
426                                 if (entry)\r
427                                 {\r
428                                         // check if the changed path would get committed by a recursive commit\r
429                                         if ((!entry->IsFromDifferentRepository()) &&\r
430                                                 (!entry->IsInExternal()) &&\r
431                                                 (!entry->IsNested()) && \r
432                                                 (!entry->IsChecked()))\r
433                                         {\r
434                                                 m_bRecursive = false;\r
435                                                 break;\r
436                                         }\r
437                                 }\r
438                         }\r
439                 }\r
440         }\r
441 \r
442 \r
443         // Now, do all the adds - make sure that the list is sorted so that parents \r
444         // are added before their children\r
445         itemsToAdd.SortByPathname();\r
446         Git Git;\r
447         if (!Git.Add(itemsToAdd, &m_ProjectProperties, Git_depth_empty, FALSE, FALSE, TRUE))\r
448         {\r
449                 CMessageBox::Show(m_hWnd, Git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
450                 InterlockedExchange(&m_bBlock, FALSE);\r
451                 Refresh();\r
452                 return;\r
453         }\r
454 \r
455         // Remove any missing items\r
456         // Not sure that this sort is really necessary - indeed, it might be better to do a reverse sort at this point\r
457         itemsToRemove.SortByPathname();\r
458         Git.Remove(itemsToRemove, TRUE);\r
459 \r
460         //the next step: find all deleted files and check if they're \r
461         //inside a deleted folder. If that's the case, then remove those\r
462         //files from the list since they'll get deleted by the parent\r
463         //folder automatically.\r
464         m_ListCtrl.Block(TRUE, FALSE);\r
465         INT_PTR nDeleted = arDeleted.GetCount();\r
466         for (INT_PTR i=0; i<arDeleted.GetCount(); i++)\r
467         {\r
468                 if (m_ListCtrl.GetCheck(arDeleted.GetAt(i)))\r
469                 {\r
470                         const CTGitPath& path = m_ListCtrl.GetListEntry(arDeleted.GetAt(i))->GetPath();\r
471                         if (path.IsDirectory())\r
472                         {\r
473                                 //now find all children of this directory\r
474                                 for (int j=0; j<arDeleted.GetCount(); j++)\r
475                                 {\r
476                                         if (i!=j)\r
477                                         {\r
478                                                 CGitStatusListCtrl::FileEntry* childEntry = m_ListCtrl.GetListEntry(arDeleted.GetAt(j));\r
479                                                 if (childEntry->IsChecked())\r
480                                                 {\r
481                                                         if (path.IsAncestorOf(childEntry->GetPath()))\r
482                                                         {\r
483                                                                 m_ListCtrl.SetEntryCheck(childEntry, arDeleted.GetAt(j), false);\r
484                                                                 nDeleted--;\r
485                                                         }\r
486                                                 }\r
487                                         }\r
488                                 }\r
489                         }\r
490                 }\r
491         } \r
492         m_ListCtrl.Block(FALSE, FALSE);\r
493 \r
494         if ((nUnchecked != 0)||(bCheckedInExternal)||(bHasConflicted)||(!m_bRecursive))\r
495         {\r
496                 //save only the files the user has checked into the temporary file\r
497                 m_ListCtrl.WriteCheckedNamesToPathList(m_pathList);\r
498         }\r
499         m_ListCtrl.WriteCheckedNamesToPathList(m_selectedPathList);\r
500         // the item count is used in the progress dialog to show the overall commit\r
501         // progress.\r
502         // deleted items only send one notification event, all others send two\r
503         m_itemsCount = ((m_selectedPathList.GetCount() - nDeleted - itemsToRemove.GetCount()) * 2) + nDeleted + itemsToRemove.GetCount();\r
504 \r
505         if ((m_bRecursive)&&(checkedLists.size() == 1))\r
506         {\r
507                 // all checked items belong to the same changelist\r
508                 // find out if there are any unchecked items which belong to that changelist\r
509                 if (uncheckedLists.find(*checkedLists.begin()) == uncheckedLists.end())\r
510                         m_sChangeList = *checkedLists.begin();\r
511         }\r
512 #endif\r
513         UpdateData();\r
514         m_regAddBeforeCommit = m_bShowUnversioned;\r
515         if (!GetDlgItem(IDC_KEEPLOCK)->IsWindowEnabled())\r
516                 m_bKeepLocks = FALSE;\r
517         m_regKeepChangelists = m_bKeepChangeList;\r
518         if (!GetDlgItem(IDC_KEEPLISTS)->IsWindowEnabled())\r
519                 m_bKeepChangeList = FALSE;\r
520         InterlockedExchange(&m_bBlock, FALSE);\r
521         m_sBugID.Trim();\r
522         if (!m_sBugID.IsEmpty())\r
523         {\r
524                 m_sBugID.Replace(_T(", "), _T(","));\r
525                 m_sBugID.Replace(_T(" ,"), _T(","));\r
526                 CString sBugID = m_ProjectProperties.sMessage;\r
527                 sBugID.Replace(_T("%BUGID%"), m_sBugID);\r
528                 if (m_ProjectProperties.bAppend)\r
529                         m_sLogMessage += _T("\n") + sBugID + _T("\n");\r
530                 else\r
531                         m_sLogMessage = sBugID + _T("\n") + m_sLogMessage;\r
532         }\r
533         m_History.AddEntry(m_sLogMessage);\r
534         m_History.Save();\r
535 \r
536         SaveSplitterPos();\r
537 \r
538         CResizableStandAloneDialog::OnOK();\r
539 }\r
540 \r
541 void CCommitDlg::SaveSplitterPos()\r
542 {\r
543         if (!IsIconic())\r
544         {\r
545                 CRegDWORD regPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\CommitDlgSizer"));\r
546                 RECT rectSplitter;\r
547                 m_wndSplitter.GetWindowRect(&rectSplitter);\r
548                 ScreenToClient(&rectSplitter);\r
549                 regPos = rectSplitter.top;\r
550         }\r
551 }\r
552 \r
553 UINT CCommitDlg::StatusThreadEntry(LPVOID pVoid)\r
554 {\r
555         return ((CCommitDlg*)pVoid)->StatusThread();\r
556 }\r
557 \r
558 UINT CCommitDlg::StatusThread()\r
559 {\r
560         //get the status of all selected file/folders recursively\r
561         //and show the ones which have to be committed to the user\r
562         //in a list control. \r
563         InterlockedExchange(&m_bBlock, TRUE);\r
564         InterlockedExchange(&m_bThreadRunning, TRUE);// so the main thread knows that this thread is still running\r
565         InterlockedExchange(&m_bRunThread, TRUE);       // if this is set to FALSE, the thread should stop\r
566         m_bCancelled = false;\r
567 \r
568         DialogEnableWindow(IDOK, false);\r
569         DialogEnableWindow(IDC_SHOWUNVERSIONED, false);\r
570         DialogEnableWindow(IDC_SELECTALL, false);\r
571         GetDlgItem(IDC_EXTERNALWARNING)->ShowWindow(SW_HIDE);\r
572         DialogEnableWindow(IDC_EXTERNALWARNING, false);\r
573 \r
574     // read the list of recent log entries before querying the WC for status\r
575     // -> the user may select one and modify / update it while we are crawling the WC\r
576 #if 0\r
577         if (m_History.GetCount()==0)\r
578         {\r
579                 CString reg;\r
580                 if (m_ListCtrl.m_sUUID.IsEmpty() && m_pathList.GetCount()>0)\r
581                 {\r
582                         Git Git;\r
583                         reg.Format(_T("Software\\TortoiseGit\\History\\commit%s"), (LPCTSTR)Git.GetUUIDFromPath(m_pathList[0]));\r
584                 }\r
585                 else\r
586                         reg.Format(_T("Software\\TortoiseGit\\History\\commit%s"), (LPCTSTR)m_ListCtrl.m_sUUID);\r
587                 m_History.Load(reg, _T("logmsgs"));\r
588         }\r
589 #endif\r
590     // Initialise the list control with the status of the files/folders below us\r
591         BOOL success = m_ListCtrl.GetStatus(m_pathList);\r
592         m_ListCtrl.CheckIfChangelistsArePresent(false);\r
593 \r
594         DWORD dwShow = SVNSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS | SVNSLC_SHOWLOCKS | SVNSLC_SHOWINCHANGELIST;\r
595         dwShow |= DWORD(m_regAddBeforeCommit) ? SVNSLC_SHOWUNVERSIONED : 0;\r
596         if (success)\r
597         {\r
598                 if (m_checkedPathList.GetCount())\r
599                         m_ListCtrl.Show(dwShow, m_checkedPathList);\r
600                 else\r
601                 {\r
602                         DWORD dwCheck = m_bSelectFilesForCommit ?SVNSLC_SHOWDIRECTS|SVNSLC_SHOWMODIFIED|SVNSLC_SHOWADDED|SVNSLC_SHOWREMOVED\r
603                                 |SVNSLC_SHOWREPLACED|SVNSLC_SHOWMERGED|SVNSLC_SHOWLOCKS : 0;\r
604                         m_ListCtrl.Show(dwShow, dwCheck);\r
605                         m_bSelectFilesForCommit = true;\r
606                 }\r
607 \r
608                 if (m_ListCtrl.HasExternalsFromDifferentRepos())\r
609                 {\r
610                         GetDlgItem(IDC_EXTERNALWARNING)->ShowWindow(SW_SHOW);\r
611                         DialogEnableWindow(IDC_EXTERNALWARNING, TRUE);\r
612                 }\r
613                 SetDlgItemText(IDC_COMMIT_TO, m_ListCtrl.m_sURL);\r
614                 m_tooltips.AddTool(GetDlgItem(IDC_STATISTICS), m_ListCtrl.GetStatisticsString());\r
615         }\r
616         CString logmsg;\r
617         GetDlgItemText(IDC_LOGMESSAGE, logmsg);\r
618         DialogEnableWindow(IDOK, logmsg.GetLength() >= m_ProjectProperties.nMinLogSize);\r
619         if (!success)\r
620         {\r
621                 if (!m_ListCtrl.GetLastErrorMessage().IsEmpty())\r
622                         m_ListCtrl.SetEmptyString(m_ListCtrl.GetLastErrorMessage());\r
623                 m_ListCtrl.Show(dwShow);\r
624         }\r
625         if ((m_ListCtrl.GetItemCount()==0)&&(m_ListCtrl.HasUnversionedItems()))\r
626         {\r
627                 if (CMessageBox::Show(m_hWnd, IDS_COMMITDLG_NOTHINGTOCOMMITUNVERSIONED, IDS_APPNAME, MB_ICONINFORMATION | MB_YESNO)==IDYES)\r
628                 {\r
629                         m_bShowUnversioned = TRUE;\r
630                         GetDlgItem(IDC_SHOWUNVERSIONED)->SendMessage(BM_SETCHECK, BST_CHECKED);\r
631                         DWORD dwShow = SVNSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS | SVNSLC_SHOWUNVERSIONED | SVNSLC_SHOWLOCKS;\r
632                         m_ListCtrl.Show(dwShow);\r
633                 }\r
634         }\r
635 \r
636         CTGitPath commonDir = m_ListCtrl.GetCommonDirectory(false);\r
637         SetWindowText(m_sWindowTitle + _T(" - ") + commonDir.GetWinPathString());\r
638 \r
639         m_autolist.clear();\r
640         // we don't have to block the commit dialog while we fetch the\r
641         // auto completion list.\r
642         m_pathwatcher.ClearChangedPaths();\r
643         InterlockedExchange(&m_bBlock, FALSE);\r
644         if ((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\Autocompletion"), TRUE)==TRUE)\r
645         {\r
646                 m_ListCtrl.Block(TRUE, TRUE);\r
647                 GetAutocompletionList();\r
648                 m_ListCtrl.Block(FALSE, FALSE);\r
649         }\r
650         if (m_bRunThread)\r
651         {\r
652                 DialogEnableWindow(IDC_SHOWUNVERSIONED, true);\r
653                 DialogEnableWindow(IDC_SELECTALL, true);\r
654                 if (m_ListCtrl.HasChangeLists())\r
655                         DialogEnableWindow(IDC_KEEPLISTS, true);\r
656                 if (m_ListCtrl.HasLocks())\r
657                         DialogEnableWindow(IDC_KEEPLOCK, true);\r
658                 // we have the list, now signal the main thread about it\r
659                 SendMessage(WM_AUTOLISTREADY);  // only send the message if the thread wasn't told to quit!\r
660         }\r
661 \r
662         InterlockedExchange(&m_bRunThread, FALSE);\r
663         InterlockedExchange(&m_bThreadRunning, FALSE);\r
664         // force the cursor to normal\r
665         RefreshCursor();\r
666 \r
667         return 0;\r
668 }\r
669 \r
670 void CCommitDlg::OnCancel()\r
671 {\r
672         m_bCancelled = true;\r
673         if (m_bBlock)\r
674                 return;\r
675         m_pathwatcher.Stop();\r
676         if (m_bThreadRunning)\r
677         {\r
678                 InterlockedExchange(&m_bRunThread, FALSE);\r
679                 WaitForSingleObject(m_pThread->m_hThread, 1000);\r
680                 if (m_bThreadRunning)\r
681                 {\r
682                         // we gave the thread a chance to quit. Since the thread didn't\r
683                         // listen to us we have to kill it.\r
684                         TerminateThread(m_pThread->m_hThread, (DWORD)-1);\r
685                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
686                 }\r
687         }\r
688         UpdateData();\r
689         m_sBugID.Trim();\r
690         m_sLogMessage = m_cLogMessage.GetText();\r
691         if (!m_sBugID.IsEmpty())\r
692         {\r
693                 m_sBugID.Replace(_T(", "), _T(","));\r
694                 m_sBugID.Replace(_T(" ,"), _T(","));\r
695                 CString sBugID = m_ProjectProperties.sMessage;\r
696                 sBugID.Replace(_T("%BUGID%"), m_sBugID);\r
697                 if (m_ProjectProperties.bAppend)\r
698                         m_sLogMessage += _T("\n") + sBugID + _T("\n");\r
699                 else\r
700                         m_sLogMessage = sBugID + _T("\n") + m_sLogMessage;\r
701         }\r
702         if (m_ProjectProperties.sLogTemplate.Compare(m_sLogMessage) != 0)\r
703                 m_History.AddEntry(m_sLogMessage);\r
704         m_History.Save();\r
705         SaveSplitterPos();\r
706         CResizableStandAloneDialog::OnCancel();\r
707 }\r
708 \r
709 void CCommitDlg::OnBnClickedSelectall()\r
710 {\r
711         m_tooltips.Pop();       // hide the tooltips\r
712         UINT state = (m_SelectAll.GetState() & 0x0003);\r
713         if (state == BST_INDETERMINATE)\r
714         {\r
715                 // It is not at all useful to manually place the checkbox into the indeterminate state...\r
716                 // We will force this on to the unchecked state\r
717                 state = BST_UNCHECKED;\r
718                 m_SelectAll.SetCheck(state);\r
719         }\r
720 //      m_ListCtrl.SelectAll(state == BST_CHECKED);\r
721 }\r
722 \r
723 BOOL CCommitDlg::PreTranslateMessage(MSG* pMsg)\r
724 {\r
725         if (!m_bBlock)\r
726                 m_tooltips.RelayEvent(pMsg);\r
727         if (pMsg->message == WM_KEYDOWN)\r
728         {\r
729                 switch (pMsg->wParam)\r
730                 {\r
731                 case VK_F5:\r
732                         {\r
733                                 if (m_bBlock)\r
734                                         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
735                                 Refresh();\r
736                         }\r
737                         break;\r
738                 case VK_RETURN:\r
739                         {\r
740                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
741                                 {\r
742                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
743                                         {\r
744                                                 PostMessage(WM_COMMAND, IDOK);\r
745                                         }\r
746                                         return TRUE;\r
747                                 }\r
748                                 if ( GetFocus()==GetDlgItem(IDC_BUGID) )\r
749                                 {\r
750                                         // Pressing RETURN in the bug id control\r
751                                         // moves the focus to the message\r
752                                         GetDlgItem(IDC_LOGMESSAGE)->SetFocus();\r
753                                         return TRUE;\r
754                                 }\r
755                         }\r
756                         break;\r
757                 }\r
758         }\r
759 \r
760         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
761 }\r
762 \r
763 void CCommitDlg::Refresh()\r
764 {\r
765         if (m_bThreadRunning)\r
766                 return;\r
767 \r
768         InterlockedExchange(&m_bBlock, TRUE);\r
769         m_pThread = AfxBeginThread(StatusThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);\r
770         if (m_pThread==NULL)\r
771         {\r
772                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
773                 InterlockedExchange(&m_bBlock, FALSE);\r
774         }\r
775         else\r
776         {\r
777                 m_pThread->m_bAutoDelete = FALSE;\r
778                 m_pThread->ResumeThread();\r
779         }\r
780 }\r
781 \r
782 void CCommitDlg::OnBnClickedHelp()\r
783 {\r
784         OnHelp();\r
785 }\r
786 \r
787 void CCommitDlg::OnBnClickedShowunversioned()\r
788 {\r
789 #if 0\r
790         m_tooltips.Pop();       // hide the tooltips\r
791         UpdateData();\r
792         m_regAddBeforeCommit = m_bShowUnversioned;\r
793         if (!m_bBlock)\r
794         {\r
795                 DWORD dwShow = m_ListCtrl.GetShowFlags();\r
796                 if (DWORD(m_regAddBeforeCommit))\r
797                         dwShow |= GitSLC_SHOWUNVERSIONED;\r
798                 else\r
799                         dwShow &= ~GitSLC_SHOWUNVERSIONED;\r
800                 m_ListCtrl.Show(dwShow);\r
801         }\r
802 #endif\r
803 }\r
804 \r
805 void CCommitDlg::OnStnClickedExternalwarning()\r
806 {\r
807         m_tooltips.Popup();\r
808 }\r
809 \r
810 void CCommitDlg::OnEnChangeLogmessage()\r
811 {\r
812         UpdateOKButton();\r
813 }\r
814 \r
815 LRESULT CCommitDlg::OnGitStatusListCtrlItemCountChanged(WPARAM, LPARAM)\r
816 {\r
817 #if 0\r
818         if ((m_ListCtrl.GetItemCount() == 0)&&(m_ListCtrl.HasUnversionedItems())&&(!m_bShowUnversioned))\r
819         {\r
820                 if (CMessageBox::Show(*this, IDS_COMMITDLG_NOTHINGTOCOMMITUNVERSIONED, IDS_APPNAME, MB_ICONINFORMATION | MB_YESNO)==IDYES)\r
821                 {\r
822                         m_bShowUnversioned = TRUE;\r
823                         DWORD dwShow = GitSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS | GitSLC_SHOWUNVERSIONED | GitSLC_SHOWLOCKS;\r
824                         m_ListCtrl.Show(dwShow);\r
825                         UpdateData(FALSE);\r
826                 }\r
827         }\r
828 #endif\r
829         return 0;\r
830 }\r
831 \r
832 LRESULT CCommitDlg::OnGitStatusListCtrlNeedsRefresh(WPARAM, LPARAM)\r
833 {\r
834         Refresh();\r
835         return 0;\r
836 }\r
837 \r
838 LRESULT CCommitDlg::OnFileDropped(WPARAM, LPARAM lParam)\r
839 {\r
840 #if 0\r
841         BringWindowToTop();\r
842         SetForegroundWindow();\r
843         SetActiveWindow();\r
844         // if multiple files/folders are dropped\r
845         // this handler is called for every single item\r
846         // separately.\r
847         // To avoid creating multiple refresh threads and\r
848         // causing crashes, we only add the items to the\r
849         // list control and start a timer.\r
850         // When the timer expires, we start the refresh thread,\r
851         // but only if it isn't already running - otherwise we\r
852         // restart the timer.\r
853         CTGitPath path;\r
854         path.SetFromWin((LPCTSTR)lParam);\r
855 \r
856         // just add all the items we get here.\r
857         // if the item is versioned, the add will fail but nothing\r
858         // more will happen.\r
859         Git Git;\r
860         Git.Add(CTGitPathList(path), &m_ProjectProperties, Git_depth_empty, false, true, true);\r
861 \r
862         if (!m_ListCtrl.HasPath(path))\r
863         {\r
864                 if (m_pathList.AreAllPathsFiles())\r
865                 {\r
866                         m_pathList.AddPath(path);\r
867                         m_pathList.RemoveDuplicates();\r
868                         m_updatedPathList.AddPath(path);\r
869                         m_updatedPathList.RemoveDuplicates();\r
870                 }\r
871                 else\r
872                 {\r
873                         // if the path list contains folders, we have to check whether\r
874                         // our just (maybe) added path is a child of one of those. If it is\r
875                         // a child of a folder already in the list, we must not add it. Otherwise\r
876                         // that path could show up twice in the list.\r
877                         bool bHasParentInList = false;\r
878                         for (int i=0; i<m_pathList.GetCount(); ++i)\r
879                         {\r
880                                 if (m_pathList[i].IsAncestorOf(path))\r
881                                 {\r
882                                         bHasParentInList = true;\r
883                                         break;\r
884                                 }\r
885                         }\r
886                         if (!bHasParentInList)\r
887                         {\r
888                                 m_pathList.AddPath(path);\r
889                                 m_pathList.RemoveDuplicates();\r
890                                 m_updatedPathList.AddPath(path);\r
891                                 m_updatedPathList.RemoveDuplicates();\r
892                         }\r
893                 }\r
894         }\r
895         \r
896         // Always start the timer, since the status of an existing item might have changed\r
897         SetTimer(REFRESHTIMER, 200, NULL);\r
898         ATLTRACE(_T("Item %s dropped, timer started\n"), path.GetWinPath());\r
899 #endif\r
900         return 0;\r
901 }\r
902 \r
903 LRESULT CCommitDlg::OnAutoListReady(WPARAM, LPARAM)\r
904 {\r
905         m_cLogMessage.SetAutoCompletionList(m_autolist, '*');\r
906         return 0;\r
907 }\r
908 \r
909 //////////////////////////////////////////////////////////////////////////\r
910 // functions which run in the status thread\r
911 //////////////////////////////////////////////////////////////////////////\r
912 \r
913 void CCommitDlg::ParseRegexFile(const CString& sFile, std::map<CString, CString>& mapRegex)\r
914 {\r
915         CString strLine;\r
916         try\r
917         {\r
918                 CStdioFile file(sFile, CFile::typeText | CFile::modeRead | CFile::shareDenyWrite);\r
919                 while (m_bRunThread && file.ReadString(strLine))\r
920                 {\r
921                         int eqpos = strLine.Find('=');\r
922                         CString rgx;\r
923                         rgx = strLine.Mid(eqpos+1).Trim();\r
924 \r
925                         int pos = -1;\r
926                         while (((pos = strLine.Find(','))>=0)&&(pos < eqpos))\r
927                         {\r
928                                 mapRegex[strLine.Left(pos)] = rgx;\r
929                                 strLine = strLine.Mid(pos+1).Trim();\r
930                         }\r
931                         mapRegex[strLine.Left(strLine.Find('=')).Trim()] = rgx;\r
932                 }\r
933                 file.Close();\r
934         }\r
935         catch (CFileException* pE)\r
936         {\r
937                 TRACE("CFileException loading auto list regex file\n");\r
938                 pE->Delete();\r
939                 return;\r
940         }\r
941 }\r
942 void CCommitDlg::GetAutocompletionList()\r
943 {\r
944         // the auto completion list is made of strings from each selected files.\r
945         // the strings used are extracted from the files with regexes found\r
946         // in the file "autolist.txt".\r
947         // the format of that file is:\r
948         // file extensions separated with commas '=' regular expression to use\r
949         // example:\r
950         // .h, .hpp = (?<=class[\s])\b\w+\b|(\b\w+(?=[\s ]?\(\);))\r
951         // .cpp = (?<=[^\s]::)\b\w+\b\r
952         \r
953         std::map<CString, CString> mapRegex;\r
954         CString sRegexFile = CPathUtils::GetAppDirectory();\r
955         CRegDWORD regtimeout = CRegDWORD(_T("Software\\TortoiseGit\\AutocompleteParseTimeout"), 5);\r
956         DWORD timeoutvalue = regtimeout*1000;\r
957         sRegexFile += _T("autolist.txt");\r
958         if (!m_bRunThread)\r
959                 return;\r
960         ParseRegexFile(sRegexFile, mapRegex);\r
961         SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, sRegexFile.GetBuffer(MAX_PATH+1));\r
962         sRegexFile.ReleaseBuffer();\r
963         sRegexFile += _T("\\TortoiseGit\\autolist.txt");\r
964         if (PathFileExists(sRegexFile))\r
965         {\r
966                 ParseRegexFile(sRegexFile, mapRegex);\r
967         }\r
968         DWORD starttime = GetTickCount();\r
969 \r
970         // now we have two arrays of strings, where the first array contains all\r
971         // file extensions we can use and the second the corresponding regex strings\r
972         // to apply to those files.\r
973 \r
974         // the next step is to go over all files shown in the commit dialog\r
975         // and scan them for strings we can use\r
976         int nListItems = m_ListCtrl.GetItemCount();\r
977 \r
978         for (int i=0; i<nListItems && m_bRunThread; ++i)\r
979         {\r
980                 // stop parsing after timeout\r
981                 if ((!m_bRunThread) || (GetTickCount() - starttime > timeoutvalue))\r
982                         return;\r
983 \r
984 //              const CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(i);\r
985 //              if (!entry)\r
986 //                      continue;\r
987                 \r
988                 // add the path parts to the auto completion list too\r
989 //              CString sPartPath = entry->GetRelativeGitPath();\r
990 //              m_autolist.insert(sPartPath);\r
991 \r
992 #if 0\r
993                 int pos = 0;\r
994                 int lastPos = 0;\r
995                 while ((pos = sPartPath.Find('/', pos)) >= 0)\r
996                 {\r
997                         pos++;\r
998                         lastPos = pos;\r
999                         m_autolist.insert(sPartPath.Mid(pos));\r
1000                 }\r
1001 \r
1002                 // Last inserted entry is a file name.\r
1003                 // Some users prefer to also list file name without extension.\r
1004                 if (CRegDWORD(_T("Software\\TortoiseGit\\AutocompleteRemovesExtensions"), FALSE))\r
1005                 {\r
1006                         int dotPos = sPartPath.ReverseFind('.');\r
1007                         if ((dotPos >= 0) && (dotPos > lastPos))\r
1008                                 m_autolist.insert(sPartPath.Mid(lastPos, dotPos - lastPos));\r
1009                 }\r
1010 \r
1011                 if ((entry->status <= Git_wc_status_normal)||(entry->status == Git_wc_status_ignored))\r
1012                         continue;\r
1013 \r
1014                 CString sExt = entry->GetPath().GetFileExtension();\r
1015                 sExt.MakeLower();\r
1016                 // find the regex string which corresponds to the file extension\r
1017                 CString rdata = mapRegex[sExt];\r
1018                 if (rdata.IsEmpty())\r
1019                         continue;\r
1020 \r
1021                 ScanFile(entry->GetPath().GetWinPathString(), rdata);\r
1022                 if ((entry->textstatus != Git_wc_status_unversioned) &&\r
1023                         (entry->textstatus != Git_wc_status_none) &&\r
1024                         (entry->textstatus != Git_wc_status_ignored) &&\r
1025                         (entry->textstatus != Git_wc_status_added) &&\r
1026                         (entry->textstatus != Git_wc_status_normal))\r
1027                 {\r
1028                         CTGitPath basePath = Git::GetPristinePath(entry->GetPath());\r
1029                         if (!basePath.IsEmpty())\r
1030                                 ScanFile(basePath.GetWinPathString(), rdata);\r
1031                 }\r
1032 #endif\r
1033         }\r
1034         ATLTRACE(_T("Auto completion list loaded in %d msec\n"), GetTickCount() - starttime);\r
1035 }\r
1036 \r
1037 void CCommitDlg::ScanFile(const CString& sFilePath, const CString& sRegex)\r
1038 {\r
1039         wstring sFileContent;\r
1040         HANDLE hFile = CreateFile(sFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);\r
1041         if (hFile != INVALID_HANDLE_VALUE)\r
1042         {\r
1043                 DWORD size = GetFileSize(hFile, NULL);\r
1044                 if (size > 1000000L)\r
1045                 {\r
1046                         // no files bigger than 1 Meg\r
1047                         CloseHandle(hFile);\r
1048                         return;\r
1049                 }\r
1050                 // allocate memory to hold file contents\r
1051                 char * buffer = new char[size];\r
1052                 DWORD readbytes;\r
1053                 ReadFile(hFile, buffer, size, &readbytes, NULL);\r
1054                 CloseHandle(hFile);\r
1055                 int opts = 0;\r
1056                 IsTextUnicode(buffer, readbytes, &opts);\r
1057                 if (opts & IS_TEXT_UNICODE_NULL_BYTES)\r
1058                 {\r
1059                         delete [] buffer;\r
1060                         return;\r
1061                 }\r
1062                 if (opts & IS_TEXT_UNICODE_UNICODE_MASK)\r
1063                 {\r
1064                         sFileContent = wstring((wchar_t*)buffer, readbytes/sizeof(WCHAR));\r
1065                 }\r
1066                 if ((opts & IS_TEXT_UNICODE_NOT_UNICODE_MASK)||(opts == 0))\r
1067                 {\r
1068                         int ret = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)buffer, readbytes, NULL, 0);\r
1069                         wchar_t * pWideBuf = new wchar_t[ret];\r
1070                         int ret2 = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)buffer, readbytes, pWideBuf, ret);\r
1071                         if (ret2 == ret)\r
1072                                 sFileContent = wstring(pWideBuf, ret);\r
1073                         delete [] pWideBuf;\r
1074                 }\r
1075                 delete [] buffer;\r
1076         }\r
1077         if (sFileContent.empty()|| !m_bRunThread)\r
1078         {\r
1079                 return;\r
1080         }\r
1081 \r
1082         try\r
1083         {\r
1084                 const tr1::wregex regCheck(sRegex, tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);\r
1085                 const tr1::wsregex_iterator end;\r
1086                 wstring s = sFileContent;\r
1087                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
1088                 {\r
1089                         const tr1::wsmatch match = *it;\r
1090                         for (size_t i=1; i<match.size(); ++i)\r
1091                         {\r
1092                                 if (match[i].second-match[i].first)\r
1093                                 {\r
1094                                         ATLTRACE(_T("matched keyword : %s\n"), wstring(match[i]).c_str());\r
1095                                         m_autolist.insert(wstring(match[i]).c_str());\r
1096                                 }\r
1097                         }\r
1098                 }\r
1099         }\r
1100         catch (exception) {}\r
1101 }\r
1102 \r
1103 // CSciEditContextMenuInterface\r
1104 void CCommitDlg::InsertMenuItems(CMenu& mPopup, int& nCmd)\r
1105 {\r
1106         CString sMenuItemText(MAKEINTRESOURCE(IDS_COMMITDLG_POPUP_PASTEFILELIST));\r
1107         m_nPopupPasteListCmd = nCmd++;\r
1108         mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nPopupPasteListCmd, sMenuItemText);\r
1109 }\r
1110 \r
1111 bool CCommitDlg::HandleMenuItemClick(int cmd, CSciEdit * pSciEdit)\r
1112 {\r
1113 #if 0\r
1114         if (m_bBlock)\r
1115                 return false;\r
1116         if (cmd == m_nPopupPasteListCmd)\r
1117         {\r
1118                 CString logmsg;\r
1119                 TCHAR buf[MAX_STATUS_STRING_LENGTH];\r
1120                 int nListItems = m_ListCtrl.GetItemCount();\r
1121                 for (int i=0; i<nListItems; ++i)\r
1122                 {\r
1123                         CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(i);\r
1124                         if (entry->IsChecked())\r
1125                         {\r
1126                                 CString line;\r
1127                                 Git_wc_status_kind status = entry->status;\r
1128                                 if (status == Git_wc_status_unversioned)\r
1129                                         status = Git_wc_status_added;\r
1130                                 if (status == Git_wc_status_missing)\r
1131                                         status = Git_wc_status_deleted;\r
1132                                 WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());\r
1133                                 if (m_ProjectProperties.bFileListInEnglish)\r
1134                                         langID = 1033;\r
1135                                 GitStatus::GetStatusString(AfxGetResourceHandle(), status, buf, sizeof(buf)/sizeof(TCHAR), langID);\r
1136                                 line.Format(_T("%-10s %s\r\n"), buf, (LPCTSTR)m_ListCtrl.GetItemText(i,0));\r
1137                                 logmsg += line;\r
1138                         }\r
1139                 }\r
1140                 pSciEdit->InsertText(logmsg);\r
1141                 return true;\r
1142         }\r
1143 #endif\r
1144         return false;\r
1145 }\r
1146 \r
1147 void CCommitDlg::OnTimer(UINT_PTR nIDEvent)\r
1148 {\r
1149         switch (nIDEvent)\r
1150         {\r
1151         case ENDDIALOGTIMER:\r
1152                 KillTimer(ENDDIALOGTIMER);\r
1153                 EndDialog(0);\r
1154                 break;\r
1155         case REFRESHTIMER:\r
1156                 if (m_bThreadRunning)\r
1157                 {\r
1158                         SetTimer(REFRESHTIMER, 200, NULL);\r
1159                         ATLTRACE("Wait some more before refreshing\n");\r
1160                 }\r
1161                 else\r
1162                 {\r
1163                         KillTimer(REFRESHTIMER);\r
1164                         ATLTRACE("Refreshing after items dropped\n");\r
1165                         Refresh();\r
1166                 }\r
1167                 break;\r
1168         }\r
1169         __super::OnTimer(nIDEvent);\r
1170 }\r
1171 \r
1172 void CCommitDlg::OnBnClickedHistory()\r
1173 {\r
1174         m_tooltips.Pop();       // hide the tooltips\r
1175         if (m_pathList.GetCount() == 0)\r
1176                 return;\r
1177 #if 0\r
1178         CHistoryDlg historyDlg;\r
1179         historyDlg.SetHistory(m_History);\r
1180         if (historyDlg.DoModal() != IDOK)\r
1181                 return;\r
1182 \r
1183         CString sMsg = historyDlg.GetSelectedText();\r
1184         if (sMsg != m_cLogMessage.GetText().Left(sMsg.GetLength()))\r
1185         {\r
1186                 CString sBugID = m_ProjectProperties.GetBugIDFromLog(sMsg);\r
1187                 if (!sBugID.IsEmpty())\r
1188                 {\r
1189                         SetDlgItemText(IDC_BUGID, sBugID);\r
1190                 }\r
1191                 if (m_ProjectProperties.sLogTemplate.Compare(m_cLogMessage.GetText())!=0)\r
1192                         m_cLogMessage.InsertText(sMsg, !m_cLogMessage.GetText().IsEmpty());\r
1193                 else\r
1194                         m_cLogMessage.SetText(sMsg);\r
1195         }\r
1196 \r
1197         UpdateOKButton();\r
1198         GetDlgItem(IDC_LOGMESSAGE)->SetFocus();\r
1199 #endif\r
1200 }\r
1201 \r
1202 void CCommitDlg::OnBnClickedBugtraqbutton()\r
1203 {\r
1204 #if 0\r
1205         m_tooltips.Pop();       // hide the tooltips\r
1206         CString sMsg = m_cLogMessage.GetText();\r
1207 \r
1208         if (m_BugTraqProvider == NULL)\r
1209                 return;\r
1210 \r
1211         BSTR parameters = m_bugtraq_association.GetParameters().AllocSysString();\r
1212         BSTR commonRoot = SysAllocString(m_pathList.GetCommonRoot().GetDirectory().GetWinPath());\r
1213         SAFEARRAY *pathList = SafeArrayCreateVector(VT_BSTR, 0, m_pathList.GetCount());\r
1214 \r
1215         for (LONG index = 0; index < m_pathList.GetCount(); ++index)\r
1216                 SafeArrayPutElement(pathList, &index, m_pathList[index].GetGitPathString().AllocSysString());\r
1217 \r
1218         BSTR originalMessage = sMsg.AllocSysString();\r
1219         BSTR temp = NULL;\r
1220 \r
1221         // first try the IBugTraqProvider2 interface\r
1222         CComPtr<IBugTraqProvider2> pProvider2 = NULL;\r
1223         HRESULT hr = m_BugTraqProvider.QueryInterface(&pProvider2);\r
1224         if (SUCCEEDED(hr))\r
1225         {\r
1226                 CString common = m_ListCtrl.GetCommonURL(false).GetGitPathString();\r
1227                 BSTR repositoryRoot = common.AllocSysString();\r
1228                 if (FAILED(hr = pProvider2->GetCommitMessage2(GetSafeHwnd(), parameters, repositoryRoot, commonRoot, pathList, originalMessage, &temp)))\r
1229                 {\r
1230                         CString sErr;\r
1231                         sErr.Format(IDS_ERR_FAILEDISSUETRACKERCOM, m_bugtraq_association.GetProviderName(), _com_error(hr).ErrorMessage());\r
1232                         CMessageBox::Show(m_hWnd, sErr, _T("TortoiseGit"), MB_ICONERROR);\r
1233                 }\r
1234                 else\r
1235                         m_cLogMessage.SetText(temp);\r
1236         }\r
1237         else\r
1238         {\r
1239                 // if IBugTraqProvider2 failed, try IBugTraqProvider\r
1240                 CComPtr<IBugTraqProvider> pProvider = NULL;\r
1241                 HRESULT hr = m_BugTraqProvider.QueryInterface(&pProvider);\r
1242                 if (FAILED(hr))\r
1243                 {\r
1244                         CString sErr;\r
1245                         sErr.Format(IDS_ERR_FAILEDISSUETRACKERCOM, (LPCTSTR)m_bugtraq_association.GetProviderName(), _com_error(hr).ErrorMessage());\r
1246                         CMessageBox::Show(m_hWnd, sErr, _T("TortoiseGit"), MB_ICONERROR);\r
1247                         return;\r
1248                 }\r
1249 \r
1250                 if (FAILED(hr = pProvider->GetCommitMessage(GetSafeHwnd(), parameters, commonRoot, pathList, originalMessage, &temp)))\r
1251                 {\r
1252                         CString sErr;\r
1253                         sErr.Format(IDS_ERR_FAILEDISSUETRACKERCOM, m_bugtraq_association.GetProviderName(), _com_error(hr).ErrorMessage());\r
1254                         CMessageBox::Show(m_hWnd, sErr, _T("TortoiseGit"), MB_ICONERROR);\r
1255                 }\r
1256                 else\r
1257                         m_cLogMessage.SetText(temp);\r
1258         }\r
1259 \r
1260         m_cLogMessage.SetFocus();\r
1261 \r
1262         SysFreeString(temp);\r
1263 #endif\r
1264 }\r
1265 \r
1266 LRESULT CCommitDlg::OnGitStatusListCtrlCheckChanged(WPARAM, LPARAM)\r
1267 {\r
1268         UpdateOKButton();\r
1269         return 0;\r
1270 }\r
1271 \r
1272 void CCommitDlg::UpdateOKButton()\r
1273 {\r
1274 #if 0\r
1275         BOOL bValidLogSize = FALSE;\r
1276 \r
1277     if (m_cLogMessage.GetText().GetLength() >= m_ProjectProperties.nMinLogSize)\r
1278                 bValidLogSize = !m_bBlock;\r
1279 \r
1280         LONG nSelectedItems = m_ListCtrl.GetSelected();\r
1281         DialogEnableWindow(IDOK, bValidLogSize && nSelectedItems>0);\r
1282 #endif\r
1283 }\r
1284 \r
1285 \r
1286 LRESULT CCommitDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)\r
1287 {\r
1288         switch (message) {\r
1289         case WM_NOTIFY:\r
1290                 if (wParam == IDC_SPLITTER)\r
1291                 { \r
1292                         SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;\r
1293                         DoSize(pHdr->delta);\r
1294                 }\r
1295                 break;\r
1296         }\r
1297 \r
1298         return __super::DefWindowProc(message, wParam, lParam);\r
1299 }\r
1300 \r
1301 void CCommitDlg::SetSplitterRange()\r
1302 {\r
1303         if ((m_ListCtrl)&&(m_cLogMessage))\r
1304         {\r
1305                 CRect rcTop;\r
1306                 m_cLogMessage.GetWindowRect(rcTop);\r
1307                 ScreenToClient(rcTop);\r
1308                 CRect rcMiddle;\r
1309                 m_ListCtrl.GetWindowRect(rcMiddle);\r
1310                 ScreenToClient(rcMiddle);\r
1311                 if (rcMiddle.Height() && rcMiddle.Width())\r
1312                         m_wndSplitter.SetRange(rcTop.top+60, rcMiddle.bottom-80);\r
1313         }\r
1314 }\r
1315 \r
1316 void CCommitDlg::DoSize(int delta)\r
1317 {\r
1318         RemoveAnchor(IDC_MESSAGEGROUP);\r
1319         RemoveAnchor(IDC_LOGMESSAGE);\r
1320         RemoveAnchor(IDC_SPLITTER);\r
1321         RemoveAnchor(IDC_LISTGROUP);\r
1322         RemoveAnchor(IDC_FILELIST);\r
1323         CSplitterControl::ChangeHeight(&m_cLogMessage, delta, CW_TOPALIGN);\r
1324         CSplitterControl::ChangeHeight(GetDlgItem(IDC_MESSAGEGROUP), delta, CW_TOPALIGN);\r
1325         CSplitterControl::ChangeHeight(&m_ListCtrl, -delta, CW_BOTTOMALIGN);\r
1326         CSplitterControl::ChangeHeight(GetDlgItem(IDC_LISTGROUP), -delta, CW_BOTTOMALIGN);\r
1327         AddAnchor(IDC_MESSAGEGROUP, TOP_LEFT, TOP_RIGHT);\r
1328         AddAnchor(IDC_LOGMESSAGE, TOP_LEFT, TOP_RIGHT);\r
1329         AddAnchor(IDC_SPLITTER, TOP_LEFT, TOP_RIGHT);\r
1330         AddAnchor(IDC_LISTGROUP, TOP_LEFT, BOTTOM_RIGHT);\r
1331         AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);\r
1332         ArrangeLayout();\r
1333         // adjust the minimum size of the dialog to prevent the resizing from\r
1334         // moving the list control too far down.\r
1335         CRect rcLogMsg;\r
1336         m_cLogMessage.GetClientRect(rcLogMsg);\r
1337         SetMinTrackSize(CSize(m_DlgOrigRect.Width(), m_DlgOrigRect.Height()-m_LogMsgOrigRect.Height()+rcLogMsg.Height()));\r
1338 \r
1339         SetSplitterRange();\r
1340         m_cLogMessage.Invalidate();\r
1341         GetDlgItem(IDC_LOGMESSAGE)->Invalidate();\r
1342 }\r
1343 \r
1344 void CCommitDlg::OnSize(UINT nType, int cx, int cy)\r
1345 {\r
1346     // first, let the resizing take place\r
1347     __super::OnSize(nType, cx, cy);\r
1348 \r
1349     //set range\r
1350     SetSplitterRange();\r
1351 }\r
1352 \r
1353 \r
1354 void CCommitDlg::OnBnClickedSignOff()\r
1355 {\r
1356         // TODO: Add your control notification handler code here\r
1357         CGit git;\r
1358         CString str;\r
1359         str.Format(_T("Signed-off-by: %s <%s>\n"),git.GetUserName(), git.GetUserEmail());\r
1360         m_cLogMessage.InsertText(str,true);\r
1361 }\r