OSDN Git Service

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