OSDN Git Service

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