OSDN Git Service

Stick Patch View basic work at
[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 "ProjectProperties.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 #include "UnicodeUtils.h"\r
35 #include "ProgressDlg.h"\r
36 #include "ShellUpdater.h"\r
37 #include "Commands/PushCommand.h"\r
38 #include "PatchViewDlg.h"\r
39 \r
40 #ifdef _DEBUG\r
41 #define new DEBUG_NEW\r
42 #undef THIS_FILE\r
43 static char THIS_FILE[] = __FILE__;\r
44 #endif\r
45 \r
46 UINT CCommitDlg::WM_AUTOLISTREADY = RegisterWindowMessage(_T("TORTOISEGIT_AUTOLISTREADY_MSG"));\r
47 \r
48 IMPLEMENT_DYNAMIC(CCommitDlg, CResizableStandAloneDialog)\r
49 CCommitDlg::CCommitDlg(CWnd* pParent /*=NULL*/)\r
50         : CResizableStandAloneDialog(CCommitDlg::IDD, pParent)\r
51         , m_bRecursive(FALSE)\r
52         , m_bShowUnversioned(FALSE)\r
53         , m_bBlock(FALSE)\r
54         , m_bThreadRunning(FALSE)\r
55         , m_bRunThread(FALSE)\r
56         , m_pThread(NULL)\r
57         , m_bWholeProject(FALSE)\r
58         , m_bKeepChangeList(TRUE)\r
59         , m_itemsCount(0)\r
60         , m_bSelectFilesForCommit(TRUE)\r
61 {\r
62         this->m_bCommitAmend=FALSE;\r
63         m_bPushAfterCommit = FALSE;\r
64 }\r
65 \r
66 CCommitDlg::~CCommitDlg()\r
67 {\r
68         if(m_pThread != NULL)\r
69         {\r
70                 delete m_pThread;\r
71         }\r
72 }\r
73 \r
74 void CCommitDlg::DoDataExchange(CDataExchange* pDX)\r
75 {\r
76         CResizableStandAloneDialog::DoDataExchange(pDX);\r
77         DDX_Control(pDX, IDC_FILELIST, m_ListCtrl);\r
78         DDX_Control(pDX, IDC_LOGMESSAGE, m_cLogMessage);\r
79         DDX_Check(pDX, IDC_SHOWUNVERSIONED, m_bShowUnversioned);\r
80         DDX_Control(pDX, IDC_SELECTALL, m_SelectAll);\r
81         DDX_Text(pDX, IDC_BUGID, m_sBugID);\r
82         DDX_Check(pDX, IDC_WHOLE_PROJECT, m_bWholeProject);\r
83         DDX_Control(pDX, IDC_SPLITTER, m_wndSplitter);\r
84         DDX_Check(pDX, IDC_KEEPLISTS, m_bKeepChangeList);\r
85         DDX_Check(pDX,IDC_COMMIT_AMEND,m_bCommitAmend);\r
86         DDX_Control(pDX,IDC_VIEW_PATCH,m_ctrlShowPatch);\r
87 }\r
88 \r
89 BEGIN_MESSAGE_MAP(CCommitDlg, CResizableStandAloneDialog)\r
90         ON_BN_CLICKED(IDC_SELECTALL, OnBnClickedSelectall)\r
91         ON_BN_CLICKED(IDHELP, OnBnClickedHelp)\r
92         ON_BN_CLICKED(IDC_SHOWUNVERSIONED, OnBnClickedShowunversioned)\r
93         ON_NOTIFY(SCN_UPDATEUI, IDC_LOGMESSAGE, OnScnUpdateUI)\r
94 //      ON_BN_CLICKED(IDC_HISTORY, OnBnClickedHistory)\r
95         ON_BN_CLICKED(IDC_BUGTRAQBUTTON, OnBnClickedBugtraqbutton)\r
96         ON_EN_CHANGE(IDC_LOGMESSAGE, OnEnChangeLogmessage)\r
97         ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_ITEMCOUNTCHANGED, OnGitStatusListCtrlItemCountChanged)\r
98         ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_NEEDSREFRESH, OnGitStatusListCtrlNeedsRefresh)\r
99         ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_ADDFILE, OnFileDropped)\r
100         ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_CHECKCHANGED, &CCommitDlg::OnGitStatusListCtrlCheckChanged)\r
101         ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_ITEMCHANGED, &CCommitDlg::OnGitStatusListCtrlItemChanged)\r
102         \r
103         ON_REGISTERED_MESSAGE(WM_AUTOLISTREADY, OnAutoListReady) \r
104         ON_WM_TIMER()\r
105     ON_WM_SIZE()\r
106         ON_STN_CLICKED(IDC_EXTERNALWARNING, &CCommitDlg::OnStnClickedExternalwarning)\r
107         ON_BN_CLICKED(IDC_SIGNOFF, &CCommitDlg::OnBnClickedSignOff)\r
108         ON_STN_CLICKED(IDC_COMMITLABEL, &CCommitDlg::OnStnClickedCommitlabel)\r
109     ON_BN_CLICKED(IDC_COMMIT_AMEND, &CCommitDlg::OnBnClickedCommitAmend)\r
110     ON_BN_CLICKED(IDC_WHOLE_PROJECT, &CCommitDlg::OnBnClickedWholeProject)\r
111         ON_STN_CLICKED(IDC_BUGIDLABEL, &CCommitDlg::OnStnClickedBugidlabel)\r
112         ON_COMMAND(ID_FOCUS_MESSAGE,&CCommitDlg::OnFocusMessage)\r
113         ON_STN_CLICKED(IDC_VIEW_PATCH, &CCommitDlg::OnStnClickedViewPatch)\r
114         ON_WM_MOVE()\r
115         ON_WM_MOVING()\r
116         ON_WM_SIZING()\r
117         ON_NOTIFY(HDN_ITEMCHANGED, 0, &CCommitDlg::OnHdnItemchangedFilelist)\r
118 END_MESSAGE_MAP()\r
119 \r
120 BOOL CCommitDlg::OnInitDialog()\r
121 {\r
122         CResizableStandAloneDialog::OnInitDialog();\r
123         \r
124         m_regAddBeforeCommit = CRegDWORD(_T("Software\\TortoiseGit\\AddBeforeCommit"), TRUE);\r
125         m_bShowUnversioned = m_regAddBeforeCommit;\r
126 \r
127         m_History.SetMaxHistoryItems((LONG)CRegDWORD(_T("Software\\TortoiseGit\\MaxHistoryItems"), 25));\r
128 \r
129         m_regKeepChangelists = CRegDWORD(_T("Software\\TortoiseGit\\KeepChangeLists"), FALSE);\r
130         m_bKeepChangeList = m_regKeepChangelists;\r
131 \r
132         m_hAccel = LoadAccelerators(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_ACC_COMMITDLG));\r
133 \r
134 //      GitConfig config;\r
135 //      m_bWholeProject = config.KeepLocks();\r
136 \r
137         if(this->m_pathList.GetCount() == 0)\r
138                 m_bWholeProject =true;\r
139         \r
140         if(this->m_pathList.GetCount() == 1 && m_pathList[0].IsEmpty())\r
141                 m_bWholeProject =true;\r
142 \r
143         UpdateData(FALSE);\r
144         \r
145         m_ListCtrl.Init(SVNSLC_COLEXT | SVNSLC_COLSTATUS | SVNSLC_COLADD |SVNSLC_COLDEL, _T("CommitDlg"));\r
146         m_ListCtrl.SetSelectButton(&m_SelectAll);\r
147         m_ListCtrl.SetStatLabel(GetDlgItem(IDC_STATISTICS));\r
148         m_ListCtrl.SetCancelBool(&m_bCancelled);\r
149         m_ListCtrl.SetEmptyString(IDS_COMMITDLG_NOTHINGTOCOMMIT);\r
150         m_ListCtrl.EnableFileDrop();\r
151         m_ListCtrl.SetBackgroundImage(IDI_COMMIT_BKG);\r
152         \r
153         //this->DialogEnableWindow(IDC_COMMIT_AMEND,FALSE);\r
154         m_ProjectProperties.ReadPropsPathList(m_pathList);\r
155 \r
156         m_cLogMessage.Init(m_ProjectProperties);\r
157         m_cLogMessage.SetFont((CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));\r
158         m_cLogMessage.RegisterContextMenuHandler(this);\r
159 \r
160         OnEnChangeLogmessage();\r
161 \r
162         m_tooltips.Create(this);\r
163         m_tooltips.AddTool(IDC_EXTERNALWARNING, IDS_COMMITDLG_EXTERNALS);\r
164         m_tooltips.AddTool(IDC_COMMIT_AMEND,IDS_COMMIT_AMEND_TT);\r
165 //      m_tooltips.AddTool(IDC_HISTORY, IDS_COMMITDLG_HISTORY_TT);\r
166         \r
167         m_SelectAll.SetCheck(BST_INDETERMINATE);\r
168 \r
169 \r
170         CBugTraqAssociations bugtraq_associations;\r
171         bugtraq_associations.Load();\r
172 \r
173         if (bugtraq_associations.FindProvider(g_Git.m_CurrentDir, &m_bugtraq_association))\r
174         {\r
175                 GetDlgItem(IDC_BUGID)->ShowWindow(SW_HIDE);\r
176                 GetDlgItem(IDC_BUGIDLABEL)->ShowWindow(SW_HIDE);\r
177 \r
178                 CComPtr<IBugTraqProvider> pProvider;\r
179                 HRESULT hr = pProvider.CoCreateInstance(m_bugtraq_association.GetProviderClass());\r
180                 if (SUCCEEDED(hr))\r
181                 {\r
182                         m_BugTraqProvider = pProvider;\r
183                         BSTR temp = NULL;\r
184                         if (SUCCEEDED(hr = pProvider->GetLinkText(GetSafeHwnd(), m_bugtraq_association.GetParameters().AllocSysString(), &temp)))\r
185                         {\r
186                                 SetDlgItemText(IDC_BUGTRAQBUTTON, temp);\r
187                                 GetDlgItem(IDC_BUGTRAQBUTTON)->EnableWindow(TRUE);\r
188                                 GetDlgItem(IDC_BUGTRAQBUTTON)->ShowWindow(SW_SHOW);\r
189                         }\r
190 \r
191                         SysFreeString(temp);\r
192                 }\r
193 \r
194                 GetDlgItem(IDC_LOGMESSAGE)->SetFocus();\r
195         }\r
196         else if (!m_ProjectProperties.sMessage.IsEmpty())\r
197         {\r
198                 GetDlgItem(IDC_BUGID)->ShowWindow(SW_SHOW);\r
199                 GetDlgItem(IDC_BUGIDLABEL)->ShowWindow(SW_SHOW);\r
200                 if (!m_ProjectProperties.sLabel.IsEmpty())\r
201                         SetDlgItemText(IDC_BUGIDLABEL, m_ProjectProperties.sLabel);\r
202                 GetDlgItem(IDC_BUGTRAQBUTTON)->ShowWindow(SW_HIDE);\r
203                 GetDlgItem(IDC_BUGTRAQBUTTON)->EnableWindow(FALSE);\r
204                 GetDlgItem(IDC_BUGID)->SetFocus();\r
205                 CString sBugID = m_ProjectProperties.GetBugIDFromLog(m_sLogMessage);\r
206                 if (!sBugID.IsEmpty())\r
207                 {\r
208                         SetDlgItemText(IDC_BUGID, sBugID);\r
209                 }\r
210         }\r
211         else\r
212         {\r
213                 GetDlgItem(IDC_BUGID)->ShowWindow(SW_HIDE);\r
214                 GetDlgItem(IDC_BUGIDLABEL)->ShowWindow(SW_HIDE);\r
215                 GetDlgItem(IDC_BUGTRAQBUTTON)->ShowWindow(SW_HIDE);\r
216                 GetDlgItem(IDC_BUGTRAQBUTTON)->EnableWindow(FALSE);\r
217                 GetDlgItem(IDC_LOGMESSAGE)->SetFocus();\r
218         }\r
219 \r
220         if (!m_sLogMessage.IsEmpty())\r
221                 m_cLogMessage.SetText(m_sLogMessage);\r
222                 \r
223         GetWindowText(m_sWindowTitle);\r
224         \r
225         AdjustControlSize(IDC_SHOWUNVERSIONED);\r
226         AdjustControlSize(IDC_SELECTALL);\r
227         AdjustControlSize(IDC_WHOLE_PROJECT);\r
228 \r
229         GetClientRect(m_DlgOrigRect);\r
230         m_cLogMessage.GetClientRect(m_LogMsgOrigRect);\r
231 \r
232         AddAnchor(IDC_COMMITLABEL, TOP_LEFT, TOP_RIGHT);\r
233         AddAnchor(IDC_BUGIDLABEL, TOP_RIGHT);\r
234         AddAnchor(IDC_BUGID, TOP_RIGHT);\r
235         AddAnchor(IDC_BUGTRAQBUTTON, TOP_RIGHT);\r
236         AddAnchor(IDC_COMMIT_TO, TOP_LEFT, TOP_RIGHT);\r
237         AddAnchor(IDC_MESSAGEGROUP, TOP_LEFT, TOP_RIGHT);\r
238 //      AddAnchor(IDC_HISTORY, TOP_LEFT);\r
239         AddAnchor(IDC_LOGMESSAGE, TOP_LEFT, TOP_RIGHT);\r
240         AddAnchor(IDC_SIGNOFF,   TOP_RIGHT);\r
241         AddAnchor(IDC_VIEW_PATCH,TOP_RIGHT);\r
242         AddAnchor(IDC_LISTGROUP, TOP_LEFT, BOTTOM_RIGHT);\r
243         AddAnchor(IDC_SPLITTER, TOP_LEFT, TOP_RIGHT);\r
244         AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);\r
245         AddAnchor(IDC_SHOWUNVERSIONED, BOTTOM_LEFT);\r
246         AddAnchor(IDC_SELECTALL, BOTTOM_LEFT);\r
247         AddAnchor(IDC_EXTERNALWARNING, BOTTOM_RIGHT);\r
248         AddAnchor(IDC_STATISTICS, BOTTOM_LEFT, BOTTOM_RIGHT);\r
249         AddAnchor(IDC_TEXT_INFO,  TOP_RIGHT);\r
250         AddAnchor(IDC_WHOLE_PROJECT, BOTTOM_LEFT);\r
251         AddAnchor(IDC_KEEPLISTS, BOTTOM_LEFT);\r
252         AddAnchor(IDOK, BOTTOM_RIGHT);\r
253         AddAnchor(IDCANCEL, BOTTOM_RIGHT);\r
254         AddAnchor(IDHELP, BOTTOM_RIGHT);\r
255         AddAnchor(IDC_COMMIT_AMEND,TOP_LEFT);\r
256         \r
257         if (hWndExplorer)\r
258                 CenterWindow(CWnd::FromHandle(hWndExplorer));\r
259         EnableSaveRestore(_T("CommitDlg"));\r
260         DWORD yPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\CommitDlgSizer"));\r
261         RECT rcDlg, rcLogMsg, rcFileList;\r
262         GetClientRect(&rcDlg);\r
263         m_cLogMessage.GetWindowRect(&rcLogMsg);\r
264         ScreenToClient(&rcLogMsg);\r
265         m_ListCtrl.GetWindowRect(&rcFileList);\r
266         ScreenToClient(&rcFileList);\r
267         if (yPos)\r
268         {\r
269                 RECT rectSplitter;\r
270                 m_wndSplitter.GetWindowRect(&rectSplitter);\r
271                 ScreenToClient(&rectSplitter);\r
272                 int delta = yPos - rectSplitter.top;\r
273                 if ((rcLogMsg.bottom + delta > rcLogMsg.top)&&(rcLogMsg.bottom + delta < rcFileList.bottom - 30))\r
274                 {\r
275                         m_wndSplitter.SetWindowPos(NULL, 0, yPos, 0, 0, SWP_NOSIZE);\r
276                         DoSize(delta);\r
277                 }\r
278         }\r
279 \r
280         // add all directories to the watcher\r
281         for (int i=0; i<m_pathList.GetCount(); ++i)\r
282         {\r
283                 if (m_pathList[i].IsDirectory())\r
284                         m_pathwatcher.AddPath(m_pathList[i]);\r
285         }\r
286 \r
287         m_updatedPathList = m_pathList;\r
288 \r
289         //first start a thread to obtain the file list with the status without\r
290         //blocking the dialog\r
291         InterlockedExchange(&m_bBlock, TRUE);\r
292         m_pThread = AfxBeginThread(StatusThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);\r
293         if (m_pThread==NULL)\r
294         {\r
295                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
296                 InterlockedExchange(&m_bBlock, FALSE);\r
297         }\r
298         else\r
299         {\r
300                 m_pThread->m_bAutoDelete = FALSE;\r
301                 m_pThread->ResumeThread();\r
302         }\r
303         CRegDWORD err = CRegDWORD(_T("Software\\TortoiseGit\\ErrorOccurred"), FALSE);\r
304         CRegDWORD historyhint = CRegDWORD(_T("Software\\TortoiseGit\\HistoryHintShown"), FALSE);\r
305         if ((((DWORD)err)!=FALSE)&&((((DWORD)historyhint)==FALSE)))\r
306         {\r
307                 historyhint = TRUE;\r
308 //              ShowBalloon(IDC_HISTORY, IDS_COMMITDLG_HISTORYHINT_TT, IDI_INFORMATION);\r
309         }\r
310         err = FALSE;\r
311 \r
312         //this->UpdateData(TRUE);\r
313         //this->m_bCommitAmend=FALSE;\r
314         //this->UpdateData(FALSE);\r
315 \r
316         this->m_ctrlShowPatch.SetURL(CString());\r
317 \r
318         return FALSE;  // return TRUE unless you set the focus to a control\r
319         // EXCEPTION: OCX Property Pages should return FALSE\r
320 }\r
321 \r
322 void CCommitDlg::OnOK()\r
323 {\r
324         if (m_bBlock)\r
325                 return;\r
326         if (m_bThreadRunning)\r
327         {\r
328                 m_bCancelled = true;\r
329                 InterlockedExchange(&m_bRunThread, FALSE);\r
330                 WaitForSingleObject(m_pThread->m_hThread, 1000);\r
331                 if (m_bThreadRunning)\r
332                 {\r
333                         // we gave the thread a chance to quit. Since the thread didn't\r
334                         // listen to us we have to kill it.\r
335                         TerminateThread(m_pThread->m_hThread, (DWORD)-1);\r
336                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
337                 }\r
338         }\r
339         this->UpdateData();\r
340 \r
341         CString id;\r
342         GetDlgItemText(IDC_BUGID, id);\r
343         if (!m_ProjectProperties.CheckBugID(id))\r
344         {\r
345                 ShowBalloon(IDC_BUGID, IDS_COMMITDLG_ONLYNUMBERS, IDI_EXCLAMATION);\r
346                 return;\r
347         }\r
348         m_sLogMessage = m_cLogMessage.GetText();\r
349         if ( m_sLogMessage.IsEmpty() )\r
350         {\r
351                 // no message entered, go round again\r
352                 CMessageBox::Show(this->m_hWnd, IDS_COMMITDLG_NOMESSAGE, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
353                 return;\r
354         }\r
355         if ((m_ProjectProperties.bWarnIfNoIssue) && (id.IsEmpty() && !m_ProjectProperties.HasBugID(m_sLogMessage)))\r
356         {\r
357                 if (CMessageBox::Show(this->m_hWnd, IDS_COMMITDLG_NOISSUEWARNING, IDS_APPNAME, MB_YESNO | MB_ICONWARNING)!=IDYES)\r
358                         return;\r
359         }\r
360 \r
361 #if 0\r
362         CRegDWORD regUnversionedRecurse (_T("Software\\TortoiseGit\\UnversionedRecurse"), TRUE);\r
363         if (!(DWORD)regUnversionedRecurse)\r
364         {\r
365                 // Find unversioned directories which are marked for commit. The user might expect them\r
366                 // to be added recursively since he cannot the the files. Let's ask the user if he knows\r
367                 // what he is doing.\r
368                 int nListItems = m_ListCtrl.GetItemCount();\r
369                 for (int j=0; j<nListItems; j++)\r
370                 {\r
371                         const CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(j);\r
372                         if (entry->IsChecked() && (entry->status == Git_wc_status_unversioned) && entry->IsFolder() )\r
373                         {\r
374                                 if (CMessageBox::Show(this->m_hWnd, IDS_COMMITDLG_UNVERSIONEDFOLDERWARNING, IDS_APPNAME, MB_YESNO | MB_ICONWARNING)!=IDYES)\r
375                                         return;\r
376                         }\r
377                 }\r
378         }\r
379 #endif\r
380         m_pathwatcher.Stop();\r
381         InterlockedExchange(&m_bBlock, TRUE);\r
382         CDWordArray arDeleted;\r
383         //first add all the unversioned files the user selected\r
384         //and check if all versioned files are selected\r
385         int nchecked = 0;\r
386         m_bRecursive = true;\r
387         int nListItems = m_ListCtrl.GetItemCount();\r
388 \r
389         CTGitPathList itemsToAdd;\r
390         CTGitPathList itemsToRemove;\r
391         //std::set<CString> checkedLists;\r
392         //std::set<CString> uncheckedLists;\r
393 \r
394         //CString checkedfiles;\r
395         //CString uncheckedfiles;\r
396 \r
397         CString cmd;\r
398         CString out;\r
399 \r
400         for (int j=0; j<nListItems; j++)\r
401         {\r
402                 //const CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(j);\r
403                 CTGitPath *entry = (CTGitPath*)m_ListCtrl.GetItemData(j);\r
404                 if (entry->m_Checked)\r
405                 {\r
406 #if 0\r
407                         if (entry->status == Git_wc_status_unversioned)\r
408                         {\r
409                                 itemsToAdd.AddPath(entry->GetPath());\r
410                         }\r
411                         if (entry->status == Git_wc_status_conflicted)\r
412                         {\r
413                                 bHasConflicted = true;\r
414                         }\r
415                         if (entry->status == Git_wc_status_missing)\r
416                         {\r
417                                 itemsToRemove.AddPath(entry->GetPath());\r
418                         }\r
419                         if (entry->status == Git_wc_status_deleted)\r
420                         {\r
421                                 arDeleted.Add(j);\r
422                         }\r
423                         if (entry->IsInExternal())\r
424                         {\r
425                                 bCheckedInExternal = true;\r
426                         }\r
427 #endif\r
428                         if( entry->m_Action & CTGitPath::LOGACTIONS_UNVER)\r
429                                 cmd.Format(_T("git.exe add -f -- \"%s\""),entry->GetGitPathString());\r
430                         else if ( entry->m_Action & CTGitPath::LOGACTIONS_DELETED)\r
431                                 cmd.Format(_T("git.exe update-index --remove -- \"%s\""),entry->GetGitPathString());\r
432                         else\r
433                                 cmd.Format(_T("git.exe update-index  -- \"%s\""),entry->GetGitPathString());\r
434 \r
435                         g_Git.Run(cmd,&out,CP_ACP);\r
436                         nchecked++;\r
437 \r
438                         //checkedLists.insert(entry->GetGitPathString());\r
439 //                      checkedfiles += _T("\"")+entry->GetGitPathString()+_T("\" ");\r
440                 }\r
441                 else\r
442                 {\r
443                         //uncheckedLists.insert(entry->GetGitPathString());\r
444                         if(entry->m_Action & CTGitPath::LOGACTIONS_ADDED)\r
445                         {       //To init git repository, there are not HEAD, so we can use git reset command\r
446                                 cmd.Format(_T("git.exe rm --cache -- \"%s\""),entry->GetGitPathString());\r
447                                 g_Git.Run(cmd,&out,CP_ACP);     \r
448                         }\r
449                         else if(!( entry->m_Action & CTGitPath::LOGACTIONS_UNVER ) )\r
450                         {\r
451                                 cmd.Format(_T("git.exe reset -- \"%s\""),entry->GetGitPathString());\r
452                                 g_Git.Run(cmd,&out,CP_ACP);\r
453                         }\r
454 \r
455                 //      uncheckedfiles += _T("\"")+entry->GetGitPathString()+_T("\" ");\r
456 #if 0\r
457                         if ((entry->status != Git_wc_status_unversioned)        &&\r
458                                 (entry->status != Git_wc_status_ignored))\r
459                         {\r
460                                 nUnchecked++;\r
461                                 uncheckedLists.insert(entry->GetChangeList());\r
462                                 if (m_bRecursive)\r
463                                 {\r
464                                         // This algorithm is for the sake of simplicity of the complexity O(N?\r
465                                         for (int k=0; k<nListItems; k++)\r
466                                         {\r
467                                                 const CGitStatusListCtrl::FileEntry * entryK = m_ListCtrl.GetListEntry(k);\r
468                                                 if (entryK->IsChecked() && entryK->GetPath().IsAncestorOf(entry->GetPath())  )\r
469                                                 {\r
470                                                         // Fall back to a non-recursive commit to prevent items being\r
471                                                         // committed which aren't checked although its parent is checked\r
472                                                         // (property change, directory deletion, ... )\r
473                                                         m_bRecursive = false;\r
474                                                         break;\r
475                                                 }\r
476                                         }\r
477                                 }\r
478                         }\r
479 #endif\r
480                 }\r
481 \r
482                 CShellUpdater::Instance().AddPathForUpdate(*entry);\r
483         }\r
484 \r
485         //if(uncheckedfiles.GetLength()>0)\r
486         //{\r
487         //      cmd.Format(_T("git.exe reset -- %s"),uncheckedfiles);\r
488         //      g_Git.Run(cmd,&out);\r
489         //}\r
490         \r
491         m_sBugID.Trim();\r
492         if (!m_sBugID.IsEmpty())\r
493         {\r
494                 m_sBugID.Replace(_T(", "), _T(","));\r
495                 m_sBugID.Replace(_T(" ,"), _T(","));\r
496                 CString sBugID = m_ProjectProperties.sMessage;\r
497                 sBugID.Replace(_T("%BUGID%"), m_sBugID);\r
498                 if (m_ProjectProperties.bAppend)\r
499                         m_sLogMessage += _T("\n") + sBugID + _T("\n");\r
500                 else\r
501                         m_sLogMessage = sBugID + _T("\n") + m_sLogMessage;\r
502         }\r
503 \r
504         BOOL bCloseCommitDlg=true;\r
505         //if(checkedfiles.GetLength()>0)\r
506         if(nchecked||m_bCommitAmend)\r
507         {\r
508         //      cmd.Format(_T("git.exe update-index -- %s"),checkedfiles);\r
509         //      g_Git.Run(cmd,&out);\r
510 \r
511                 CString tempfile=::GetTempFile();\r
512                 \r
513                 CAppUtils::SaveCommitUnicodeFile(tempfile,m_sLogMessage);\r
514                 //file.WriteString(m_sLogMessage);\r
515                                 \r
516         \r
517                 out =_T("");\r
518                 CString amend;\r
519                 if(this->m_bCommitAmend)\r
520                 {\r
521                         amend=_T("--amend");\r
522                 }\r
523                 cmd.Format(_T("git.exe commit %s -F \"%s\""),amend, tempfile);\r
524                 \r
525                 CProgressDlg progress;\r
526                 progress.m_bBufferAll=true; // improve show speed when there are many file added. \r
527                 progress.m_GitCmd=cmd;\r
528                 progress.m_bShowCommand = FALSE;        // don't show the commit command\r
529                 progress.m_PreText = out;                       // show any output already generated in log window\r
530                 progress.m_changeAbortButtonOnSuccessTo = _T("&Push");\r
531                 DWORD userResponse = progress.DoModal();\r
532                 \r
533                 if(progress.m_GitStatus)\r
534                 {\r
535                         bCloseCommitDlg = false;\r
536                         this->Refresh();\r
537                 }\r
538                 else if(userResponse == IDC_PROGRESS_BUTTON1)\r
539                 {\r
540                         //User pressed 'Push' button after successful commit.\r
541                         m_bPushAfterCommit=true;\r
542                 }\r
543 \r
544                 CFile::Remove(tempfile);\r
545         }else\r
546         {\r
547                 CMessageBox::Show(this->m_hWnd, IDS_ERROR_NOTHING_COMMIT, IDS_COMMIT_FINISH, MB_OK | MB_ICONINFORMATION);\r
548                 bCloseCommitDlg=false;\r
549         }\r
550 #if 0\r
551         if (m_pathwatcher.GetNumberOfChangedPaths() && m_bRecursive)\r
552         {\r
553                 // There are paths which got changed (touched at least).\r
554                 // We have to find out if this affects the selection in the commit dialog\r
555                 // If it could affect the selection, revert back to a non-recursive commit\r
556                 CTGitPathList changedList = m_pathwatcher.GetChangedPaths();\r
557                 changedList.RemoveDuplicates();\r
558                 for (int i=0; i<changedList.GetCount(); ++i)\r
559                 {\r
560                         if (changedList[i].IsAdminDir())\r
561                         {\r
562                                 // something inside an admin dir was changed.\r
563                                 // if it's the entries file, then we have to fully refresh because\r
564                                 // files may have been added/removed from version control\r
565                                 if ((changedList[i].GetWinPathString().Right(7).CompareNoCase(_T("entries")) == 0) &&\r
566                                         (changedList[i].GetWinPathString().Find(_T("\\tmp\\"))<0))\r
567                                 {\r
568                                         m_bRecursive = false;\r
569                                         break;\r
570                                 }\r
571                         }\r
572                         else if (!m_ListCtrl.IsPathShown(changedList[i]))\r
573                         {\r
574                                 // a path which is not shown in the list has changed\r
575                                 CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(changedList[i]);\r
576                                 if (entry)\r
577                                 {\r
578                                         // check if the changed path would get committed by a recursive commit\r
579                                         if ((!entry->IsFromDifferentRepository()) &&\r
580                                                 (!entry->IsInExternal()) &&\r
581                                                 (!entry->IsNested()) && \r
582                                                 (!entry->IsChecked()))\r
583                                         {\r
584                                                 m_bRecursive = false;\r
585                                                 break;\r
586                                         }\r
587                                 }\r
588                         }\r
589                 }\r
590         }\r
591 \r
592 \r
593         // Now, do all the adds - make sure that the list is sorted so that parents \r
594         // are added before their children\r
595         itemsToAdd.SortByPathname();\r
596         Git Git;\r
597         if (!Git.Add(itemsToAdd, &m_ProjectProperties, Git_depth_empty, FALSE, FALSE, TRUE))\r
598         {\r
599                 CMessageBox::Show(m_hWnd, Git.GetLastErrorMessage(), _T("TortoiseGit"), MB_ICONERROR);\r
600                 InterlockedExchange(&m_bBlock, FALSE);\r
601                 Refresh();\r
602                 return;\r
603         }\r
604 \r
605         // Remove any missing items\r
606         // Not sure that this sort is really necessary - indeed, it might be better to do a reverse sort at this point\r
607         itemsToRemove.SortByPathname();\r
608         Git.Remove(itemsToRemove, TRUE);\r
609 \r
610         //the next step: find all deleted files and check if they're \r
611         //inside a deleted folder. If that's the case, then remove those\r
612         //files from the list since they'll get deleted by the parent\r
613         //folder automatically.\r
614         m_ListCtrl.Block(TRUE, FALSE);\r
615         INT_PTR nDeleted = arDeleted.GetCount();\r
616         for (INT_PTR i=0; i<arDeleted.GetCount(); i++)\r
617         {\r
618                 if (m_ListCtrl.GetCheck(arDeleted.GetAt(i)))\r
619                 {\r
620                         const CTGitPath& path = m_ListCtrl.GetListEntry(arDeleted.GetAt(i))->GetPath();\r
621                         if (path.IsDirectory())\r
622                         {\r
623                                 //now find all children of this directory\r
624                                 for (int j=0; j<arDeleted.GetCount(); j++)\r
625                                 {\r
626                                         if (i!=j)\r
627                                         {\r
628                                                 CGitStatusListCtrl::FileEntry* childEntry = m_ListCtrl.GetListEntry(arDeleted.GetAt(j));\r
629                                                 if (childEntry->IsChecked())\r
630                                                 {\r
631                                                         if (path.IsAncestorOf(childEntry->GetPath()))\r
632                                                         {\r
633                                                                 m_ListCtrl.SetEntryCheck(childEntry, arDeleted.GetAt(j), false);\r
634                                                                 nDeleted--;\r
635                                                         }\r
636                                                 }\r
637                                         }\r
638                                 }\r
639                         }\r
640                 }\r
641         } \r
642         m_ListCtrl.Block(FALSE, FALSE);\r
643 \r
644         if ((nUnchecked != 0)||(bCheckedInExternal)||(bHasConflicted)||(!m_bRecursive))\r
645         {\r
646                 //save only the files the user has checked into the temporary file\r
647                 m_ListCtrl.WriteCheckedNamesToPathList(m_pathList);\r
648         }\r
649         m_ListCtrl.WriteCheckedNamesToPathList(m_selectedPathList);\r
650         // the item count is used in the progress dialog to show the overall commit\r
651         // progress.\r
652         // deleted items only send one notification event, all others send two\r
653         m_itemsCount = ((m_selectedPathList.GetCount() - nDeleted - itemsToRemove.GetCount()) * 2) + nDeleted + itemsToRemove.GetCount();\r
654 \r
655         if ((m_bRecursive)&&(checkedLists.size() == 1))\r
656         {\r
657                 // all checked items belong to the same changelist\r
658                 // find out if there are any unchecked items which belong to that changelist\r
659                 if (uncheckedLists.find(*checkedLists.begin()) == uncheckedLists.end())\r
660                         m_sChangeList = *checkedLists.begin();\r
661         }\r
662 #endif\r
663         UpdateData();\r
664         m_regAddBeforeCommit = m_bShowUnversioned;\r
665         if (!GetDlgItem(IDC_WHOLE_PROJECT)->IsWindowEnabled())\r
666                 m_bWholeProject = FALSE;\r
667         m_regKeepChangelists = m_bKeepChangeList;\r
668         if (!GetDlgItem(IDC_KEEPLISTS)->IsWindowEnabled())\r
669                 m_bKeepChangeList = FALSE;\r
670         InterlockedExchange(&m_bBlock, FALSE);\r
671 \r
672         m_History.AddEntry(m_sLogMessage);\r
673         m_History.Save();\r
674 \r
675         SaveSplitterPos();\r
676 \r
677         if( bCloseCommitDlg )\r
678                 CResizableStandAloneDialog::OnOK();\r
679 \r
680         CShellUpdater::Instance().Flush();\r
681 }\r
682 \r
683 void CCommitDlg::SaveSplitterPos()\r
684 {\r
685         if (!IsIconic())\r
686         {\r
687                 CRegDWORD regPos = CRegDWORD(_T("Software\\TortoiseGit\\TortoiseProc\\ResizableState\\CommitDlgSizer"));\r
688                 RECT rectSplitter;\r
689                 m_wndSplitter.GetWindowRect(&rectSplitter);\r
690                 ScreenToClient(&rectSplitter);\r
691                 regPos = rectSplitter.top;\r
692         }\r
693 }\r
694 \r
695 UINT CCommitDlg::StatusThreadEntry(LPVOID pVoid)\r
696 {\r
697         return ((CCommitDlg*)pVoid)->StatusThread();\r
698 }\r
699 \r
700 UINT CCommitDlg::StatusThread()\r
701 {\r
702         //get the status of all selected file/folders recursively\r
703         //and show the ones which have to be committed to the user\r
704         //in a list control. \r
705         InterlockedExchange(&m_bBlock, TRUE);\r
706         InterlockedExchange(&m_bThreadRunning, TRUE);// so the main thread knows that this thread is still running\r
707         InterlockedExchange(&m_bRunThread, TRUE);       // if this is set to FALSE, the thread should stop\r
708         m_bCancelled = false;\r
709 \r
710         DialogEnableWindow(IDOK, false);\r
711         DialogEnableWindow(IDC_SHOWUNVERSIONED, false);\r
712     DialogEnableWindow(IDC_WHOLE_PROJECT, false);\r
713         DialogEnableWindow(IDC_SELECTALL, false);\r
714         GetDlgItem(IDC_EXTERNALWARNING)->ShowWindow(SW_HIDE);\r
715         DialogEnableWindow(IDC_EXTERNALWARNING, false);\r
716     // read the list of recent log entries before querying the WC for status\r
717     // -> the user may select one and modify / update it while we are crawling the WC\r
718 \r
719         if (m_History.GetCount()==0)\r
720         {\r
721                 CString reg;\r
722                 reg.Format(_T("Software\\TortoiseGit\\History\\commit%s"), (LPCTSTR)m_ListCtrl.m_sUUID);\r
723                 reg.Replace(_T(':'),_T('_'));\r
724                 m_History.Load(reg, _T("logmsgs"));\r
725         }\r
726 \r
727     // Initialise the list control with the status of the files/folders below us\r
728         m_ListCtrl.Clear();\r
729         BOOL success;\r
730     if(m_bWholeProject)\r
731         success=m_ListCtrl.GetStatus(NULL);\r
732     else\r
733         success=m_ListCtrl.GetStatus(&m_pathList);\r
734 \r
735         //m_ListCtrl.UpdateFileList(git_revnum_t(GIT_REV_ZERO));\r
736         if(this->m_bShowUnversioned)\r
737                 m_ListCtrl.UpdateFileList(CGitStatusListCtrl::FILELIST_UNVER,true,&m_pathList);\r
738         \r
739         m_ListCtrl.CheckIfChangelistsArePresent(false);\r
740 \r
741         DWORD dwShow = SVNSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS | SVNSLC_SHOWLOCKS | SVNSLC_SHOWINCHANGELIST;\r
742         dwShow |= DWORD(m_regAddBeforeCommit) ? SVNSLC_SHOWUNVERSIONED : 0;\r
743         if (success)\r
744         {\r
745                 if (m_checkedPathList.GetCount())\r
746                         m_ListCtrl.Show(dwShow, m_checkedPathList);\r
747                 else\r
748                 {\r
749                         DWORD dwCheck = m_bSelectFilesForCommit ? dwShow : 0;\r
750                         dwCheck &=~(CTGitPath::LOGACTIONS_UNVER); //don't check unversion file default. \r
751                         m_ListCtrl.Show(dwShow, dwCheck);\r
752                         m_bSelectFilesForCommit = true;\r
753                 }\r
754 \r
755                 if (m_ListCtrl.HasExternalsFromDifferentRepos())\r
756                 {\r
757                         GetDlgItem(IDC_EXTERNALWARNING)->ShowWindow(SW_SHOW);\r
758                         DialogEnableWindow(IDC_EXTERNALWARNING, TRUE);\r
759                 }\r
760                 \r
761                 SetDlgItemText(IDC_COMMIT_TO, g_Git.GetCurrentBranch());\r
762                 m_tooltips.AddTool(GetDlgItem(IDC_STATISTICS), m_ListCtrl.GetStatisticsString());\r
763         }\r
764         CString logmsg;\r
765         GetDlgItemText(IDC_LOGMESSAGE, logmsg);\r
766         DialogEnableWindow(IDOK, logmsg.GetLength() >= m_ProjectProperties.nMinLogSize);\r
767         if (!success)\r
768         {\r
769                 if (!m_ListCtrl.GetLastErrorMessage().IsEmpty())\r
770                         m_ListCtrl.SetEmptyString(m_ListCtrl.GetLastErrorMessage());\r
771                 m_ListCtrl.Show(dwShow);\r
772         }\r
773         if ((m_ListCtrl.GetItemCount()==0)&&(m_ListCtrl.HasUnversionedItems()))\r
774         {\r
775                 if (CMessageBox::Show(m_hWnd, IDS_COMMITDLG_NOTHINGTOCOMMITUNVERSIONED, IDS_APPNAME, MB_ICONINFORMATION | MB_YESNO)==IDYES)\r
776                 {\r
777                         m_bShowUnversioned = TRUE;\r
778                         GetDlgItem(IDC_SHOWUNVERSIONED)->SendMessage(BM_SETCHECK, BST_CHECKED);\r
779                         DWORD dwShow = (DWORD)(SVNSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS | SVNSLC_SHOWUNVERSIONED | SVNSLC_SHOWLOCKS);\r
780                         m_ListCtrl.UpdateFileList(CGitStatusListCtrl::FILELIST_UNVER);\r
781                         m_ListCtrl.Show(dwShow,dwShow&(~CTGitPath::LOGACTIONS_UNVER));\r
782                 }\r
783         }\r
784 \r
785         CTGitPath commonDir = m_ListCtrl.GetCommonDirectory(false);\r
786 \r
787     if(this->m_bWholeProject)   \r
788         SetWindowText(m_sWindowTitle + _T(" - ") + CString(_T("Whole Project")));\r
789     else\r
790             SetWindowText(m_sWindowTitle + _T(" - ") + commonDir.GetWinPathString());\r
791 \r
792         m_autolist.clear();\r
793         // we don't have to block the commit dialog while we fetch the\r
794         // auto completion list.\r
795         m_pathwatcher.ClearChangedPaths();\r
796         InterlockedExchange(&m_bBlock, FALSE);\r
797         if ((DWORD)CRegDWORD(_T("Software\\TortoiseGit\\Autocompletion"), TRUE)==TRUE)\r
798         {\r
799                 m_ListCtrl.Block(TRUE, TRUE);\r
800                 GetAutocompletionList();\r
801                 m_ListCtrl.Block(FALSE, FALSE);\r
802         }\r
803         if (m_bRunThread)\r
804         {\r
805                 DialogEnableWindow(IDC_SHOWUNVERSIONED, true);\r
806         DialogEnableWindow(IDC_WHOLE_PROJECT, true);\r
807                 DialogEnableWindow(IDC_SELECTALL, true);\r
808                 if (m_ListCtrl.HasChangeLists())\r
809                         DialogEnableWindow(IDC_KEEPLISTS, true);\r
810                 if (m_ListCtrl.HasLocks())\r
811                         DialogEnableWindow(IDC_WHOLE_PROJECT, true);\r
812                 // we have the list, now signal the main thread about it\r
813                 SendMessage(WM_AUTOLISTREADY);  // only send the message if the thread wasn't told to quit!\r
814         }\r
815 \r
816         InterlockedExchange(&m_bRunThread, FALSE);\r
817         InterlockedExchange(&m_bThreadRunning, FALSE);\r
818         // force the cursor to normal\r
819         RefreshCursor();\r
820 \r
821         return 0;\r
822 }\r
823 \r
824 void CCommitDlg::OnCancel()\r
825 {\r
826         m_bCancelled = true;\r
827         m_pathwatcher.Stop();\r
828 \r
829         if (m_bBlock)\r
830                 return;\r
831         \r
832         if (m_bThreadRunning)\r
833         {\r
834                 InterlockedExchange(&m_bRunThread, FALSE);\r
835                 WaitForSingleObject(m_pThread->m_hThread, 1000);\r
836                 if (m_bThreadRunning)\r
837                 {\r
838                         // we gave the thread a chance to quit. Since the thread didn't\r
839                         // listen to us we have to kill it.\r
840                         TerminateThread(m_pThread->m_hThread, (DWORD)-1);\r
841                         InterlockedExchange(&m_bThreadRunning, FALSE);\r
842                 }\r
843         }\r
844         UpdateData();\r
845         m_sBugID.Trim();\r
846         m_sLogMessage = m_cLogMessage.GetText();\r
847         if (!m_sBugID.IsEmpty())\r
848         {\r
849                 m_sBugID.Replace(_T(", "), _T(","));\r
850                 m_sBugID.Replace(_T(" ,"), _T(","));\r
851                 CString sBugID = m_ProjectProperties.sMessage;\r
852                 sBugID.Replace(_T("%BUGID%"), m_sBugID);\r
853                 if (m_ProjectProperties.bAppend)\r
854                         m_sLogMessage += _T("\n") + sBugID + _T("\n");\r
855                 else\r
856                         m_sLogMessage = sBugID + _T("\n") + m_sLogMessage;\r
857         }\r
858         if (m_ProjectProperties.sLogTemplate.Compare(m_sLogMessage) != 0)\r
859                 m_History.AddEntry(m_sLogMessage);\r
860         m_History.Save();\r
861         SaveSplitterPos();\r
862         CResizableStandAloneDialog::OnCancel();\r
863 }\r
864 \r
865 void CCommitDlg::OnBnClickedSelectall()\r
866 {\r
867         m_tooltips.Pop();       // hide the tooltips\r
868         UINT state = (m_SelectAll.GetState() & 0x0003);\r
869         if (state == BST_INDETERMINATE)\r
870         {\r
871                 // It is not at all useful to manually place the checkbox into the indeterminate state...\r
872                 // We will force this on to the unchecked state\r
873                 state = BST_UNCHECKED;\r
874                 m_SelectAll.SetCheck(state);\r
875         }\r
876         m_ListCtrl.SelectAll(state == BST_CHECKED);\r
877 }\r
878 \r
879 BOOL CCommitDlg::PreTranslateMessage(MSG* pMsg)\r
880 {\r
881         if (!m_bBlock)\r
882                 m_tooltips.RelayEvent(pMsg);\r
883         \r
884         if (m_hAccel)\r
885         {\r
886                 int ret = TranslateAccelerator(m_hWnd, m_hAccel, pMsg);\r
887                 if (ret)\r
888                         return TRUE;\r
889         }\r
890 \r
891         if (pMsg->message == WM_KEYDOWN)\r
892         {\r
893                 switch (pMsg->wParam)\r
894                 {\r
895                 case VK_F5:\r
896                         {\r
897                                 if (m_bBlock)\r
898                                         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
899                                 Refresh();\r
900                         }\r
901                         break;\r
902                 case VK_RETURN:\r
903                         {\r
904                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
905                                 {\r
906                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
907                                         {\r
908                                                 PostMessage(WM_COMMAND, IDOK);\r
909                                         }\r
910                                         return TRUE;\r
911                                 }\r
912                                 if ( GetFocus()==GetDlgItem(IDC_BUGID) )\r
913                                 {\r
914                                         // Pressing RETURN in the bug id control\r
915                                         // moves the focus to the message\r
916                                         GetDlgItem(IDC_LOGMESSAGE)->SetFocus();\r
917                                         return TRUE;\r
918                                 }\r
919                         }\r
920                         break;\r
921                 }\r
922         }\r
923 \r
924         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
925 }\r
926 \r
927 void CCommitDlg::Refresh()\r
928 {\r
929         if (m_bThreadRunning)\r
930                 return;\r
931 \r
932         InterlockedExchange(&m_bBlock, TRUE);\r
933         m_pThread = AfxBeginThread(StatusThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);\r
934         if (m_pThread==NULL)\r
935         {\r
936                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
937                 InterlockedExchange(&m_bBlock, FALSE);\r
938         }\r
939         else\r
940         {\r
941                 m_pThread->m_bAutoDelete = FALSE;\r
942                 m_pThread->ResumeThread();\r
943         }\r
944 }\r
945 \r
946 void CCommitDlg::OnBnClickedHelp()\r
947 {\r
948         OnHelp();\r
949 }\r
950 \r
951 void CCommitDlg::OnBnClickedShowunversioned()\r
952 {\r
953         m_tooltips.Pop();       // hide the tooltips\r
954         UpdateData();\r
955         m_regAddBeforeCommit = m_bShowUnversioned;\r
956         if (!m_bBlock)\r
957         {\r
958                 DWORD dwShow = m_ListCtrl.GetShowFlags();\r
959                 if (DWORD(m_regAddBeforeCommit))\r
960                         dwShow |= SVNSLC_SHOWUNVERSIONED;\r
961                 else\r
962                         dwShow &= ~SVNSLC_SHOWUNVERSIONED;\r
963                 if(dwShow & SVNSLC_SHOWUNVERSIONED)\r
964                 {\r
965             if(m_bWholeProject)\r
966                 m_ListCtrl.GetStatus(NULL,false,false,true);\r
967             else\r
968                             m_ListCtrl.GetStatus(&this->m_pathList,false,false,true);\r
969                 }\r
970                 m_ListCtrl.Show(dwShow);\r
971         }\r
972 }\r
973 \r
974 void CCommitDlg::OnStnClickedExternalwarning()\r
975 {\r
976         m_tooltips.Popup();\r
977 }\r
978 \r
979 void CCommitDlg::OnEnChangeLogmessage()\r
980 {\r
981         UpdateOKButton();\r
982 }\r
983 \r
984 LRESULT CCommitDlg::OnGitStatusListCtrlItemCountChanged(WPARAM, LPARAM)\r
985 {\r
986 #if 0\r
987         if ((m_ListCtrl.GetItemCount() == 0)&&(m_ListCtrl.HasUnversionedItems())&&(!m_bShowUnversioned))\r
988         {\r
989                 if (CMessageBox::Show(*this, IDS_COMMITDLG_NOTHINGTOCOMMITUNVERSIONED, IDS_APPNAME, MB_ICONINFORMATION | MB_YESNO)==IDYES)\r
990                 {\r
991                         m_bShowUnversioned = TRUE;\r
992                         DWORD dwShow = GitSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS | GitSLC_SHOWUNVERSIONED | GitSLC_SHOWLOCKS;\r
993                         m_ListCtrl.Show(dwShow);\r
994                         UpdateData(FALSE);\r
995                 }\r
996         }\r
997 #endif\r
998         return 0;\r
999 }\r
1000 \r
1001 LRESULT CCommitDlg::OnGitStatusListCtrlNeedsRefresh(WPARAM, LPARAM)\r
1002 {\r
1003         Refresh();\r
1004         return 0;\r
1005 }\r
1006 \r
1007 LRESULT CCommitDlg::OnFileDropped(WPARAM, LPARAM /*lParam*/)\r
1008 {\r
1009 #if 0\r
1010         BringWindowToTop();\r
1011         SetForegroundWindow();\r
1012         SetActiveWindow();\r
1013         // if multiple files/folders are dropped\r
1014         // this handler is called for every single item\r
1015         // separately.\r
1016         // To avoid creating multiple refresh threads and\r
1017         // causing crashes, we only add the items to the\r
1018         // list control and start a timer.\r
1019         // When the timer expires, we start the refresh thread,\r
1020         // but only if it isn't already running - otherwise we\r
1021         // restart the timer.\r
1022         CTGitPath path;\r
1023         path.SetFromWin((LPCTSTR)lParam);\r
1024 \r
1025         // just add all the items we get here.\r
1026         // if the item is versioned, the add will fail but nothing\r
1027         // more will happen.\r
1028         Git Git;\r
1029         Git.Add(CTGitPathList(path), &m_ProjectProperties, Git_depth_empty, false, true, true);\r
1030 \r
1031         if (!m_ListCtrl.HasPath(path))\r
1032         {\r
1033                 if (m_pathList.AreAllPathsFiles())\r
1034                 {\r
1035                         m_pathList.AddPath(path);\r
1036                         m_pathList.RemoveDuplicates();\r
1037                         m_updatedPathList.AddPath(path);\r
1038                         m_updatedPathList.RemoveDuplicates();\r
1039                 }\r
1040                 else\r
1041                 {\r
1042                         // if the path list contains folders, we have to check whether\r
1043                         // our just (maybe) added path is a child of one of those. If it is\r
1044                         // a child of a folder already in the list, we must not add it. Otherwise\r
1045                         // that path could show up twice in the list.\r
1046                         bool bHasParentInList = false;\r
1047                         for (int i=0; i<m_pathList.GetCount(); ++i)\r
1048                         {\r
1049                                 if (m_pathList[i].IsAncestorOf(path))\r
1050                                 {\r
1051                                         bHasParentInList = true;\r
1052                                         break;\r
1053                                 }\r
1054                         }\r
1055                         if (!bHasParentInList)\r
1056                         {\r
1057                                 m_pathList.AddPath(path);\r
1058                                 m_pathList.RemoveDuplicates();\r
1059                                 m_updatedPathList.AddPath(path);\r
1060                                 m_updatedPathList.RemoveDuplicates();\r
1061                         }\r
1062                 }\r
1063         }\r
1064         \r
1065         // Always start the timer, since the status of an existing item might have changed\r
1066         SetTimer(REFRESHTIMER, 200, NULL);\r
1067         ATLTRACE(_T("Item %s dropped, timer started\n"), path.GetWinPath());\r
1068 #endif\r
1069         return 0;\r
1070 }\r
1071 \r
1072 LRESULT CCommitDlg::OnAutoListReady(WPARAM, LPARAM)\r
1073 {\r
1074         m_cLogMessage.SetAutoCompletionList(m_autolist, '*');\r
1075         return 0;\r
1076 }\r
1077 \r
1078 //////////////////////////////////////////////////////////////////////////\r
1079 // functions which run in the status thread\r
1080 //////////////////////////////////////////////////////////////////////////\r
1081 \r
1082 void CCommitDlg::ParseRegexFile(const CString& sFile, std::map<CString, CString>& mapRegex)\r
1083 {\r
1084         CString strLine;\r
1085         try\r
1086         {\r
1087                 CStdioFile file(sFile, CFile::typeText | CFile::modeRead | CFile::shareDenyWrite);\r
1088                 while (m_bRunThread && file.ReadString(strLine))\r
1089                 {\r
1090                         int eqpos = strLine.Find('=');\r
1091                         CString rgx;\r
1092                         rgx = strLine.Mid(eqpos+1).Trim();\r
1093 \r
1094                         int pos = -1;\r
1095                         while (((pos = strLine.Find(','))>=0)&&(pos < eqpos))\r
1096                         {\r
1097                                 mapRegex[strLine.Left(pos)] = rgx;\r
1098                                 strLine = strLine.Mid(pos+1).Trim();\r
1099                         }\r
1100                         mapRegex[strLine.Left(strLine.Find('=')).Trim()] = rgx;\r
1101                 }\r
1102                 file.Close();\r
1103         }\r
1104         catch (CFileException* pE)\r
1105         {\r
1106                 TRACE("CFileException loading auto list regex file\n");\r
1107                 pE->Delete();\r
1108                 return;\r
1109         }\r
1110 }\r
1111 void CCommitDlg::GetAutocompletionList()\r
1112 {\r
1113         // the auto completion list is made of strings from each selected files.\r
1114         // the strings used are extracted from the files with regexes found\r
1115         // in the file "autolist.txt".\r
1116         // the format of that file is:\r
1117         // file extensions separated with commas '=' regular expression to use\r
1118         // example:\r
1119         // .h, .hpp = (?<=class[\s])\b\w+\b|(\b\w+(?=[\s ]?\(\);))\r
1120         // .cpp = (?<=[^\s]::)\b\w+\b\r
1121         \r
1122         std::map<CString, CString> mapRegex;\r
1123         CString sRegexFile = CPathUtils::GetAppDirectory();\r
1124         CRegDWORD regtimeout = CRegDWORD(_T("Software\\TortoiseGit\\AutocompleteParseTimeout"), 5);\r
1125         DWORD timeoutvalue = regtimeout*1000;\r
1126         sRegexFile += _T("autolist.txt");\r
1127         if (!m_bRunThread)\r
1128                 return;\r
1129         ParseRegexFile(sRegexFile, mapRegex);\r
1130         SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, sRegexFile.GetBuffer(MAX_PATH+1));\r
1131         sRegexFile.ReleaseBuffer();\r
1132         sRegexFile += _T("\\TortoiseGit\\autolist.txt");\r
1133         if (PathFileExists(sRegexFile))\r
1134         {\r
1135                 ParseRegexFile(sRegexFile, mapRegex);\r
1136         }\r
1137         DWORD starttime = GetTickCount();\r
1138 \r
1139         // now we have two arrays of strings, where the first array contains all\r
1140         // file extensions we can use and the second the corresponding regex strings\r
1141         // to apply to those files.\r
1142 \r
1143         // the next step is to go over all files shown in the commit dialog\r
1144         // and scan them for strings we can use\r
1145         int nListItems = m_ListCtrl.GetItemCount();\r
1146 \r
1147         for (int i=0; i<nListItems && m_bRunThread; ++i)\r
1148         {\r
1149                 // stop parsing after timeout\r
1150                 if ((!m_bRunThread) || (GetTickCount() - starttime > timeoutvalue))\r
1151                         return;\r
1152 \r
1153                 CTGitPath *path = (CTGitPath*)m_ListCtrl.GetItemData(i);\r
1154 \r
1155                 if(path == NULL)\r
1156                         continue;\r
1157 \r
1158                 CString sPartPath =path->GetGitPathString();\r
1159                 m_autolist.insert(sPartPath);\r
1160 \r
1161 //              const CGitStatusListCtrl::FileEntry * entry = m_ListCtrl.GetListEntry(i);\r
1162 //              if (!entry)\r
1163 //                      continue;\r
1164                 \r
1165                 // add the path parts to the auto completion list too\r
1166 //              CString sPartPath = entry->GetRelativeGitPath();\r
1167 //              m_autolist.insert(sPartPath);\r
1168 \r
1169 \r
1170                 int pos = 0;\r
1171                 int lastPos = 0;\r
1172                 while ((pos = sPartPath.Find('/', pos)) >= 0)\r
1173                 {\r
1174                         pos++;\r
1175                         lastPos = pos;\r
1176                         m_autolist.insert(sPartPath.Mid(pos));\r
1177                 }\r
1178 \r
1179                 // Last inserted entry is a file name.\r
1180                 // Some users prefer to also list file name without extension.\r
1181                 if (CRegDWORD(_T("Software\\TortoiseGit\\AutocompleteRemovesExtensions"), FALSE))\r
1182                 {\r
1183                         int dotPos = sPartPath.ReverseFind('.');\r
1184                         if ((dotPos >= 0) && (dotPos > lastPos))\r
1185                                 m_autolist.insert(sPartPath.Mid(lastPos, dotPos - lastPos));\r
1186                 }\r
1187 #if 0\r
1188                 if ((entry->status <= Git_wc_status_normal)||(entry->status == Git_wc_status_ignored))\r
1189                         continue;\r
1190 \r
1191                 CString sExt = entry->GetPath().GetFileExtension();\r
1192                 sExt.MakeLower();\r
1193                 // find the regex string which corresponds to the file extension\r
1194                 CString rdata = mapRegex[sExt];\r
1195                 if (rdata.IsEmpty())\r
1196                         continue;\r
1197 \r
1198                 ScanFile(entry->GetPath().GetWinPathString(), rdata);\r
1199                 if ((entry->textstatus != Git_wc_status_unversioned) &&\r
1200                         (entry->textstatus != Git_wc_status_none) &&\r
1201                         (entry->textstatus != Git_wc_status_ignored) &&\r
1202                         (entry->textstatus != Git_wc_status_added) &&\r
1203                         (entry->textstatus != Git_wc_status_normal))\r
1204                 {\r
1205                         CTGitPath basePath = Git::GetPristinePath(entry->GetPath());\r
1206                         if (!basePath.IsEmpty())\r
1207                                 ScanFile(basePath.GetWinPathString(), rdata);\r
1208                 }\r
1209 #endif\r
1210         }\r
1211         ATLTRACE(_T("Auto completion list loaded in %d msec\n"), GetTickCount() - starttime);\r
1212 }\r
1213 \r
1214 void CCommitDlg::ScanFile(const CString& sFilePath, const CString& sRegex)\r
1215 {\r
1216         wstring sFileContent;\r
1217         HANDLE hFile = CreateFile(sFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);\r
1218         if (hFile != INVALID_HANDLE_VALUE)\r
1219         {\r
1220                 DWORD size = GetFileSize(hFile, NULL);\r
1221                 if (size > 1000000L)\r
1222                 {\r
1223                         // no files bigger than 1 Meg\r
1224                         CloseHandle(hFile);\r
1225                         return;\r
1226                 }\r
1227                 // allocate memory to hold file contents\r
1228                 char * buffer = new char[size];\r
1229                 DWORD readbytes;\r
1230                 ReadFile(hFile, buffer, size, &readbytes, NULL);\r
1231                 CloseHandle(hFile);\r
1232                 int opts = 0;\r
1233                 IsTextUnicode(buffer, readbytes, &opts);\r
1234                 if (opts & IS_TEXT_UNICODE_NULL_BYTES)\r
1235                 {\r
1236                         delete [] buffer;\r
1237                         return;\r
1238                 }\r
1239                 if (opts & IS_TEXT_UNICODE_UNICODE_MASK)\r
1240                 {\r
1241                         sFileContent = wstring((wchar_t*)buffer, readbytes/sizeof(WCHAR));\r
1242                 }\r
1243                 if ((opts & IS_TEXT_UNICODE_NOT_UNICODE_MASK)||(opts == 0))\r
1244                 {\r
1245                         int ret = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)buffer, readbytes, NULL, 0);\r
1246                         wchar_t * pWideBuf = new wchar_t[ret];\r
1247                         int ret2 = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)buffer, readbytes, pWideBuf, ret);\r
1248                         if (ret2 == ret)\r
1249                                 sFileContent = wstring(pWideBuf, ret);\r
1250                         delete [] pWideBuf;\r
1251                 }\r
1252                 delete [] buffer;\r
1253         }\r
1254         if (sFileContent.empty()|| !m_bRunThread)\r
1255         {\r
1256                 return;\r
1257         }\r
1258 \r
1259         try\r
1260         {\r
1261                 const tr1::wregex regCheck(sRegex, tr1::regex_constants::icase | tr1::regex_constants::ECMAScript);\r
1262                 const tr1::wsregex_iterator end;\r
1263                 wstring s = sFileContent;\r
1264                 for (tr1::wsregex_iterator it(s.begin(), s.end(), regCheck); it != end; ++it)\r
1265                 {\r
1266                         const tr1::wsmatch match = *it;\r
1267                         for (size_t i=1; i<match.size(); ++i)\r
1268                         {\r
1269                                 if (match[i].second-match[i].first)\r
1270                                 {\r
1271                                         ATLTRACE(_T("matched keyword : %s\n"), wstring(match[i]).c_str());\r
1272                                         m_autolist.insert(wstring(match[i]).c_str());\r
1273                                 }\r
1274                         }\r
1275                 }\r
1276         }\r
1277         catch (exception) {}\r
1278 }\r
1279 \r
1280 // CSciEditContextMenuInterface\r
1281 void CCommitDlg::InsertMenuItems(CMenu& mPopup, int& nCmd)\r
1282 {\r
1283         CString sMenuItemText(MAKEINTRESOURCE(IDS_COMMITDLG_POPUP_PASTEFILELIST));\r
1284         m_nPopupPasteListCmd = nCmd++;\r
1285         mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nPopupPasteListCmd, sMenuItemText);\r
1286 \r
1287         //CString sMenuItemText(MAKEINTRESOURCE(IDS_COMMITDLG_POPUP_PASTEFILELIST));\r
1288         if(m_History.GetCount() > 0)\r
1289         {\r
1290                 sMenuItemText.LoadString(IDS_COMMITDLG_POPUP_PASTELASTMESSAGE);\r
1291                 m_nPopupPasteLastMessage = nCmd++;\r
1292                 mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nPopupPasteLastMessage, sMenuItemText);\r
1293 \r
1294                 sMenuItemText.LoadString(IDS_COMMITDLG_POPUP_LOGHISTORY);\r
1295                 m_nPopupRecentMessage = nCmd++;\r
1296                 mPopup.AppendMenu(MF_STRING | MF_ENABLED, m_nPopupRecentMessage, sMenuItemText);\r
1297 \r
1298         }\r
1299         \r
1300 }\r
1301 \r
1302 bool CCommitDlg::HandleMenuItemClick(int cmd, CSciEdit * pSciEdit)\r
1303 {\r
1304 \r
1305         if (m_bBlock)\r
1306                 return false;\r
1307         if (cmd == m_nPopupPasteListCmd)\r
1308         {\r
1309                 CString logmsg;\r
1310                 TCHAR buf[MAX_STATUS_STRING_LENGTH];\r
1311                 int nListItems = m_ListCtrl.GetItemCount();\r
1312                 for (int i=0; i<nListItems; ++i)\r
1313                 {\r
1314                         CTGitPath * entry = (CTGitPath*)m_ListCtrl.GetItemData(i);\r
1315                         if (entry&&entry->m_Checked)\r
1316                         {\r
1317                                 CString line;\r
1318                                 CString status = entry->GetActionName();\r
1319                                 if(entry->m_Action & CTGitPath::LOGACTIONS_UNVER)\r
1320                                         status = _T("Add");\r
1321 \r
1322                                 //git_wc_status_kind status = entry->status;\r
1323                                 WORD langID = (WORD)CRegStdWORD(_T("Software\\TortoiseGit\\LanguageID"), GetUserDefaultLangID());\r
1324                                 if (m_ProjectProperties.bFileListInEnglish)\r
1325                                         langID = 1033;\r
1326                                 \r
1327                                 line.Format(_T("%-10s %s\r\n"),status , (LPCTSTR)m_ListCtrl.GetItemText(i,0));\r
1328                                 logmsg += line;\r
1329                         }\r
1330                 }\r
1331                 pSciEdit->InsertText(logmsg);\r
1332                 return true;\r
1333         }\r
1334 \r
1335         if(cmd == m_nPopupPasteLastMessage)\r
1336         {\r
1337                 if(m_History.GetCount() ==0 )\r
1338                         return false;\r
1339 \r
1340                 CString logmsg;\r
1341                 logmsg +=m_History.GetEntry(0);\r
1342                 pSciEdit->InsertText(logmsg);\r
1343                 return true;\r
1344         }\r
1345 \r
1346         if(cmd == m_nPopupRecentMessage )\r
1347         {\r
1348                 OnBnClickedHistory();\r
1349                 return true;\r
1350         }\r
1351         return false;\r
1352 }\r
1353 \r
1354 void CCommitDlg::OnTimer(UINT_PTR nIDEvent)\r
1355 {\r
1356         switch (nIDEvent)\r
1357         {\r
1358         case ENDDIALOGTIMER:\r
1359                 KillTimer(ENDDIALOGTIMER);\r
1360                 EndDialog(0);\r
1361                 break;\r
1362         case REFRESHTIMER:\r
1363                 if (m_bThreadRunning)\r
1364                 {\r
1365                         SetTimer(REFRESHTIMER, 200, NULL);\r
1366                         ATLTRACE("Wait some more before refreshing\n");\r
1367                 }\r
1368                 else\r
1369                 {\r
1370                         KillTimer(REFRESHTIMER);\r
1371                         ATLTRACE("Refreshing after items dropped\n");\r
1372                         Refresh();\r
1373                 }\r
1374                 break;\r
1375         }\r
1376         __super::OnTimer(nIDEvent);\r
1377 }\r
1378 \r
1379 void CCommitDlg::OnBnClickedHistory()\r
1380 {\r
1381         m_tooltips.Pop();       // hide the tooltips\r
1382         if (m_pathList.GetCount() == 0)\r
1383                 return;\r
1384 \r
1385         CHistoryDlg historyDlg;\r
1386         historyDlg.SetHistory(m_History);\r
1387         if (historyDlg.DoModal() != IDOK)\r
1388                 return;\r
1389 \r
1390         CString sMsg = historyDlg.GetSelectedText();\r
1391         if (sMsg != m_cLogMessage.GetText().Left(sMsg.GetLength()))\r
1392         {\r
1393                 CString sBugID = m_ProjectProperties.GetBugIDFromLog(sMsg);\r
1394                 if (!sBugID.IsEmpty())\r
1395                 {\r
1396                         SetDlgItemText(IDC_BUGID, sBugID);\r
1397                 }\r
1398                 if (m_ProjectProperties.sLogTemplate.Compare(m_cLogMessage.GetText())!=0)\r
1399                         m_cLogMessage.InsertText(sMsg, !m_cLogMessage.GetText().IsEmpty());\r
1400                 else\r
1401                         m_cLogMessage.SetText(sMsg);\r
1402         }\r
1403 \r
1404         UpdateOKButton();\r
1405         GetDlgItem(IDC_LOGMESSAGE)->SetFocus();\r
1406 \r
1407 }\r
1408 \r
1409 void CCommitDlg::OnBnClickedBugtraqbutton()\r
1410 {\r
1411         m_tooltips.Pop();       // hide the tooltips\r
1412         CString sMsg = m_cLogMessage.GetText();\r
1413 \r
1414         if (m_BugTraqProvider == NULL)\r
1415                 return;\r
1416 \r
1417         BSTR parameters = m_bugtraq_association.GetParameters().AllocSysString();\r
1418         BSTR commonRoot = SysAllocString(g_Git.m_CurrentDir);\r
1419         SAFEARRAY *pathList = SafeArrayCreateVector(VT_BSTR, 0, m_pathList.GetCount());\r
1420 \r
1421         for (LONG index = 0; index < m_pathList.GetCount(); ++index)\r
1422                 SafeArrayPutElement(pathList, &index, m_pathList[index].GetGitPathString().AllocSysString());\r
1423 \r
1424         BSTR originalMessage = sMsg.AllocSysString();\r
1425         BSTR temp = NULL;\r
1426 //      m_revProps.clear();\r
1427 \r
1428         // first try the IBugTraqProvider2 interface\r
1429         CComPtr<IBugTraqProvider2> pProvider2 = NULL;\r
1430         HRESULT hr = m_BugTraqProvider.QueryInterface(&pProvider2);\r
1431         if (SUCCEEDED(hr))\r
1432         {\r
1433                 //CString common = m_ListCtrl.GetCommonURL(false).GetGitPathString();\r
1434                 BSTR repositoryRoot = g_Git.m_CurrentDir.AllocSysString();\r
1435                 BSTR bugIDOut = NULL;\r
1436                 GetDlgItemText(IDC_BUGID, m_sBugID);\r
1437                 BSTR bugID = m_sBugID.AllocSysString();\r
1438                 SAFEARRAY * revPropNames = NULL;\r
1439                 SAFEARRAY * revPropValues = NULL;\r
1440                 if (FAILED(hr = pProvider2->GetCommitMessage2(GetSafeHwnd(), parameters, repositoryRoot, commonRoot, pathList, originalMessage, bugID, &bugIDOut, &revPropNames, &revPropValues, &temp)))\r
1441                 {\r
1442                         CString sErr;\r
1443                         sErr.Format(IDS_ERR_FAILEDISSUETRACKERCOM, m_bugtraq_association.GetProviderName(), _com_error(hr).ErrorMessage());\r
1444                         CMessageBox::Show(m_hWnd, sErr, _T("TortoiseGit"), MB_ICONERROR);\r
1445                 }\r
1446                 else\r
1447                 {\r
1448                         if (bugIDOut)\r
1449                         {\r
1450                                 m_sBugID = bugIDOut;\r
1451                                 SysFreeString(bugIDOut);\r
1452                                 SetDlgItemText(IDC_BUGID, m_sBugID);\r
1453                         }\r
1454                         SysFreeString(bugID);\r
1455                         SysFreeString(repositoryRoot);\r
1456                         m_cLogMessage.SetText(temp);\r
1457                         BSTR HUGEP *pbRevNames;\r
1458                         BSTR HUGEP *pbRevValues;\r
1459 \r
1460                         HRESULT hr1 = SafeArrayAccessData(revPropNames, (void HUGEP**)&pbRevNames);\r
1461                         if (SUCCEEDED(hr1))\r
1462                         {\r
1463                                 HRESULT hr2 = SafeArrayAccessData(revPropValues, (void HUGEP**)&pbRevValues);\r
1464                                 if (SUCCEEDED(hr2))\r
1465                                 {\r
1466                                         if (revPropNames->rgsabound->cElements == revPropValues->rgsabound->cElements)\r
1467                                         {\r
1468                                                 for (ULONG i = 0; i < revPropNames->rgsabound->cElements; i++)\r
1469                                                 {\r
1470 //                                                      m_revProps[pbRevNames[i]] = pbRevValues[i];\r
1471                                                 }\r
1472                                         }\r
1473                                         SafeArrayUnaccessData(revPropValues);\r
1474                                 }\r
1475                                 SafeArrayUnaccessData(revPropNames);\r
1476                         }\r
1477                         if (revPropNames)\r
1478                                 SafeArrayDestroy(revPropNames);\r
1479                         if (revPropValues)\r
1480                                 SafeArrayDestroy(revPropValues);\r
1481                 }\r
1482         }\r
1483         else\r
1484         {\r
1485                 // if IBugTraqProvider2 failed, try IBugTraqProvider\r
1486                 CComPtr<IBugTraqProvider> pProvider = NULL;\r
1487                 hr = m_BugTraqProvider.QueryInterface(&pProvider);\r
1488                 if (FAILED(hr))\r
1489                 {\r
1490                         CString sErr;\r
1491                         sErr.Format(IDS_ERR_FAILEDISSUETRACKERCOM, (LPCTSTR)m_bugtraq_association.GetProviderName(), _com_error(hr).ErrorMessage());\r
1492                         CMessageBox::Show(m_hWnd, sErr, _T("TortoiseGit"), MB_ICONERROR);\r
1493                         return;\r
1494                 }\r
1495 \r
1496                 if (FAILED(hr = pProvider->GetCommitMessage(GetSafeHwnd(), parameters, commonRoot, pathList, originalMessage, &temp)))\r
1497                 {\r
1498                         CString sErr;\r
1499                         sErr.Format(IDS_ERR_FAILEDISSUETRACKERCOM, m_bugtraq_association.GetProviderName(), _com_error(hr).ErrorMessage());\r
1500                         CMessageBox::Show(m_hWnd, sErr, _T("TortoiseGit"), MB_ICONERROR);\r
1501                 }\r
1502                 else\r
1503                         m_cLogMessage.SetText(temp);\r
1504         }\r
1505         m_sLogMessage = m_cLogMessage.GetText();\r
1506         if (!m_ProjectProperties.sMessage.IsEmpty())\r
1507         {\r
1508                 CString sBugID = m_ProjectProperties.FindBugID(m_sLogMessage);\r
1509                 if (!sBugID.IsEmpty())\r
1510                 {\r
1511                         SetDlgItemText(IDC_BUGID, sBugID);\r
1512                 }\r
1513         }\r
1514 \r
1515         m_cLogMessage.SetFocus();\r
1516 \r
1517         SysFreeString(parameters);\r
1518         SysFreeString(commonRoot);\r
1519         SafeArrayDestroy(pathList);\r
1520         SysFreeString(originalMessage);\r
1521         SysFreeString(temp);\r
1522 \r
1523 }\r
1524 LRESULT CCommitDlg::OnGitStatusListCtrlItemChanged(WPARAM wparam, LPARAM lparam)\r
1525 {\r
1526         TRACE("OnGitStatusListCtrlItemChanged %d\r\n", wparam);\r
1527         if(::IsWindow(this->m_patchViewdlg.m_hWnd))\r
1528         {\r
1529                 m_patchViewdlg.m_ctrlPatchView.SetText(CString());\r
1530 \r
1531                 POSITION pos=m_ListCtrl.GetFirstSelectedItemPosition();\r
1532                 m_patchViewdlg.m_ctrlPatchView.Call(SCI_SETREADONLY, FALSE);\r
1533                 while(pos)\r
1534                 {\r
1535                         int nSelect = m_ListCtrl.GetNextSelectedItem(pos);\r
1536                         CTGitPath * p=(CTGitPath*)m_ListCtrl.GetItemData(nSelect);\r
1537                         if(p)\r
1538                         {\r
1539                                 CString cmd,out;\r
1540                                 cmd.Format(_T("git.exe diff -- \"%s\""),p->GetGitPathString());\r
1541                                 g_Git.Run(cmd,&out,CP_ACP);\r
1542 \r
1543                                 m_patchViewdlg.m_ctrlPatchView.SetText(out);\r
1544                         }\r
1545 \r
1546                 }\r
1547                 m_patchViewdlg.m_ctrlPatchView.Call(SCI_SETREADONLY, TRUE);\r
1548                 m_patchViewdlg.m_ctrlPatchView.Call(SCI_GOTOPOS, 0);\r
1549 \r
1550         }\r
1551         return 0;\r
1552 }\r
1553 \r
1554 \r
1555 LRESULT CCommitDlg::OnGitStatusListCtrlCheckChanged(WPARAM, LPARAM)\r
1556 {\r
1557         UpdateOKButton();\r
1558         return 0;\r
1559 }\r
1560 \r
1561 void CCommitDlg::UpdateOKButton()\r
1562 {\r
1563 #if 0\r
1564         BOOL bValidLogSize = FALSE;\r
1565 \r
1566     if (m_cLogMessage.GetText().GetLength() >= m_ProjectProperties.nMinLogSize)\r
1567                 bValidLogSize = !m_bBlock;\r
1568 \r
1569         LONG nSelectedItems = m_ListCtrl.GetSelected();\r
1570         DialogEnableWindow(IDOK, bValidLogSize && nSelectedItems>0);\r
1571 #endif\r
1572 }\r
1573 \r
1574 \r
1575 LRESULT CCommitDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)\r
1576 {\r
1577         switch (message) {\r
1578         case WM_NOTIFY:\r
1579                 if (wParam == IDC_SPLITTER)\r
1580                 { \r
1581                         SPC_NMHDR* pHdr = (SPC_NMHDR*) lParam;\r
1582                         DoSize(pHdr->delta);\r
1583                 }\r
1584                 break;\r
1585         }\r
1586 \r
1587         return __super::DefWindowProc(message, wParam, lParam);\r
1588 }\r
1589 \r
1590 void CCommitDlg::SetSplitterRange()\r
1591 {\r
1592         if ((m_ListCtrl)&&(m_cLogMessage))\r
1593         {\r
1594                 CRect rcTop;\r
1595                 m_cLogMessage.GetWindowRect(rcTop);\r
1596                 ScreenToClient(rcTop);\r
1597                 CRect rcMiddle;\r
1598                 m_ListCtrl.GetWindowRect(rcMiddle);\r
1599                 ScreenToClient(rcMiddle);\r
1600                 if (rcMiddle.Height() && rcMiddle.Width())\r
1601                         m_wndSplitter.SetRange(rcTop.top+60, rcMiddle.bottom-80);\r
1602         }\r
1603 }\r
1604 \r
1605 void CCommitDlg::DoSize(int delta)\r
1606 {\r
1607         RemoveAnchor(IDC_MESSAGEGROUP);\r
1608         RemoveAnchor(IDC_LOGMESSAGE);\r
1609         RemoveAnchor(IDC_SPLITTER);\r
1610         RemoveAnchor(IDC_SIGNOFF);\r
1611         RemoveAnchor(IDC_COMMIT_AMEND);\r
1612         RemoveAnchor(IDC_LISTGROUP);\r
1613         RemoveAnchor(IDC_FILELIST);\r
1614         RemoveAnchor(IDC_TEXT_INFO);\r
1615         RemoveAnchor(IDC_VIEW_PATCH);\r
1616 \r
1617         CSplitterControl::ChangeHeight(&m_cLogMessage, delta, CW_TOPALIGN);\r
1618         CSplitterControl::ChangeHeight(GetDlgItem(IDC_MESSAGEGROUP), delta, CW_TOPALIGN);\r
1619         CSplitterControl::ChangeHeight(&m_ListCtrl, -delta, CW_BOTTOMALIGN);\r
1620         CSplitterControl::ChangeHeight(GetDlgItem(IDC_LISTGROUP), -delta, CW_BOTTOMALIGN);\r
1621         CSplitterControl::ChangePos(GetDlgItem(IDC_SIGNOFF),0,delta);\r
1622         CSplitterControl::ChangePos(GetDlgItem(IDC_COMMIT_AMEND),0,delta);\r
1623         CSplitterControl::ChangePos(GetDlgItem(IDC_TEXT_INFO),0,delta);\r
1624         CSplitterControl::ChangePos(GetDlgItem(IDC_VIEW_PATCH),0,delta);\r
1625         \r
1626         AddAnchor(IDC_VIEW_PATCH,TOP_RIGHT);\r
1627         AddAnchor(IDC_MESSAGEGROUP, TOP_LEFT, TOP_RIGHT);\r
1628         AddAnchor(IDC_LOGMESSAGE, TOP_LEFT, TOP_RIGHT);\r
1629         AddAnchor(IDC_SPLITTER, TOP_LEFT, TOP_RIGHT);\r
1630         AddAnchor(IDC_LISTGROUP, TOP_LEFT, BOTTOM_RIGHT);\r
1631         AddAnchor(IDC_FILELIST, TOP_LEFT, BOTTOM_RIGHT);\r
1632         AddAnchor(IDC_SIGNOFF,TOP_RIGHT);\r
1633         AddAnchor(IDC_COMMIT_AMEND,TOP_LEFT);\r
1634         AddAnchor(IDC_TEXT_INFO,TOP_RIGHT);\r
1635         ArrangeLayout();\r
1636         // adjust the minimum size of the dialog to prevent the resizing from\r
1637         // moving the list control too far down.\r
1638         CRect rcLogMsg;\r
1639         m_cLogMessage.GetClientRect(rcLogMsg);\r
1640         SetMinTrackSize(CSize(m_DlgOrigRect.Width(), m_DlgOrigRect.Height()-m_LogMsgOrigRect.Height()+rcLogMsg.Height()));\r
1641 \r
1642         SetSplitterRange();\r
1643         m_cLogMessage.Invalidate();\r
1644         GetDlgItem(IDC_LOGMESSAGE)->Invalidate();\r
1645 }\r
1646 \r
1647 void CCommitDlg::OnSize(UINT nType, int cx, int cy)\r
1648 {\r
1649     // first, let the resizing take place\r
1650     __super::OnSize(nType, cx, cy);\r
1651 \r
1652     //set range\r
1653     SetSplitterRange();\r
1654 \r
1655 }\r
1656 \r
1657 \r
1658 void CCommitDlg::OnBnClickedSignOff()\r
1659 {\r
1660         // TODO: Add your control notification handler code here\r
1661         CString str;\r
1662         CString username;\r
1663         CString email;\r
1664         username=g_Git.GetUserName();\r
1665         email=g_Git.GetUserEmail();\r
1666         username.Remove(_T('\n'));\r
1667         email.Remove(_T('\n'));\r
1668         str.Format(_T("Signed-off-by: %s <%s>\n"),username,email);\r
1669 \r
1670         m_cLogMessage.SetText(m_cLogMessage.GetText()+_T("\r\n\r\n")+str);\r
1671 }\r
1672 \r
1673 void CCommitDlg::OnStnClickedCommitlabel()\r
1674 {\r
1675         // TODO: Add your control notification handler code here\r
1676 }\r
1677 \r
1678 void CCommitDlg::OnBnClickedCommitAmend()\r
1679 {\r
1680     // TODO: Add your control notification handler code here\r
1681         this->UpdateData();\r
1682         if(this->m_bCommitAmend && this->m_AmendStr.IsEmpty())\r
1683         {\r
1684                 GitRev rev;\r
1685                 BYTE_VECTOR vector;\r
1686                 CString head(_T("HEAD"));\r
1687                 g_Git.GetLog(vector,head,NULL,1);\r
1688                 rev.ParserFromLog(vector);\r
1689                 m_AmendStr=rev.m_Subject+_T("\n\n")+rev.m_Body;\r
1690         }\r
1691 \r
1692         if(this->m_bCommitAmend)\r
1693         {\r
1694                 this->m_NoAmendStr=this->m_cLogMessage.GetText();\r
1695                 m_cLogMessage.SetText(m_AmendStr);\r
1696 \r
1697         }else\r
1698         {\r
1699                 this->m_AmendStr=this->m_cLogMessage.GetText();\r
1700                 m_cLogMessage.SetText(m_NoAmendStr);\r
1701 \r
1702         }\r
1703 \r
1704 }\r
1705 \r
1706 void CCommitDlg::OnBnClickedWholeProject()\r
1707 {\r
1708     // TODO: Add your control notification handler code here\r
1709     m_tooltips.Pop();   // hide the tooltips\r
1710         UpdateData();\r
1711     m_ListCtrl.Clear();\r
1712         if (!m_bBlock)\r
1713         {\r
1714             if(m_bWholeProject)\r
1715             m_ListCtrl.GetStatus(NULL,true,false,true);\r
1716         else\r
1717                     m_ListCtrl.GetStatus(&this->m_pathList,true,false,true);\r
1718                 \r
1719                 m_ListCtrl.Show(m_ListCtrl.GetShowFlags());\r
1720         }\r
1721 \r
1722         CTGitPath commonDir = m_ListCtrl.GetCommonDirectory(false);\r
1723 \r
1724     if(this->m_bWholeProject)   \r
1725         SetWindowText(m_sWindowTitle + _T(" - ") + CString(_T("Whole Project")));\r
1726     else\r
1727             SetWindowText(m_sWindowTitle + _T(" - ") + commonDir.GetWinPathString());\r
1728 \r
1729 }\r
1730 \r
1731 void CCommitDlg::OnStnClickedBugidlabel()\r
1732 {\r
1733         // TODO: Add your control notification handler code here\r
1734 }\r
1735 \r
1736 void CCommitDlg::OnFocusMessage()\r
1737 {\r
1738         m_cLogMessage.SetFocus();\r
1739 }\r
1740 \r
1741 void CCommitDlg::OnScnUpdateUI(NMHDR *pNMHDR, LRESULT *pResult)\r
1742 {\r
1743         SCNotification *pHead =(SCNotification *)pNMHDR;\r
1744         \r
1745         int pos=this->m_cLogMessage.Call(SCI_GETCURRENTPOS);\r
1746         int line=this->m_cLogMessage.Call(SCI_LINEFROMPOSITION,pos);\r
1747         int column=this->m_cLogMessage.Call(SCI_GETCOLUMN,pos);\r
1748 \r
1749         CString str;\r
1750         str.Format(_T("%d/%d"),line+1,column+1);\r
1751         this->GetDlgItem(IDC_TEXT_INFO)->SetWindowText(str);\r
1752 \r
1753         if(*pResult)\r
1754                 *pResult=0;\r
1755 }\r
1756 void CCommitDlg::OnStnClickedViewPatch()\r
1757 {\r
1758         // TODO: Add your control notification handler code here\r
1759         \r
1760         m_patchViewdlg.m_pProjectProperties = &this->m_ProjectProperties;\r
1761         m_patchViewdlg.m_ParentCommitDlg = this;\r
1762         if(!IsWindow(this->m_patchViewdlg.m_hWnd))\r
1763         {\r
1764                 m_patchViewdlg.Create(IDD_PATCH_VIEW,this);\r
1765                 CRect rect;\r
1766                 this->GetWindowRect(&rect);\r
1767                 rect.left=rect.right;\r
1768                 rect.right=rect.left+200;\r
1769                 m_patchViewdlg.MoveWindow(rect);\r
1770                 m_patchViewdlg.ShowWindow(SW_SHOW);\r
1771                 m_patchViewdlg.MoveWindow(rect);\r
1772                 ShowViewPatchText(false);\r
1773         }\r
1774         else\r
1775         {\r
1776                 m_patchViewdlg.ShowWindow(SW_HIDE);\r
1777                 m_patchViewdlg.DestroyWindow();\r
1778                 ShowViewPatchText(true);\r
1779         }\r
1780 }\r
1781 \r
1782 void CCommitDlg::OnMove(int x, int y)\r
1783 {\r
1784         __super::OnMove(x, y);\r
1785 \r
1786         // TODO: Add your message handler code here\r
1787 }\r
1788 \r
1789 void CCommitDlg::OnMoving(UINT fwSide, LPRECT pRect)\r
1790 {\r
1791         __super::OnMoving(fwSide, pRect);\r
1792 \r
1793         // TODO: Add your message handler code here\r
1794         if (::IsWindow(m_patchViewdlg.m_hWnd))\r
1795         {\r
1796                 RECT patchrect;\r
1797                 m_patchViewdlg.GetWindowRect(&patchrect);\r
1798                 if (::IsWindow(m_hWnd))\r
1799                 {\r
1800                         RECT thisrect;\r
1801                         GetWindowRect(&thisrect);\r
1802                         if (patchrect.left == thisrect.right)\r
1803                         {\r
1804                                 m_patchViewdlg.SetWindowPos(NULL, patchrect.left - (thisrect.left - pRect->left), patchrect.top - (thisrect.top - pRect->top), \r
1805                                         0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);\r
1806                         }\r
1807                 }\r
1808         }\r
1809 \r
1810 }\r
1811 \r
1812 void CCommitDlg::OnSizing(UINT fwSide, LPRECT pRect)\r
1813 {\r
1814         __super::OnSizing(fwSide, pRect);\r
1815 \r
1816         if(::IsWindow(this->m_patchViewdlg.m_hWnd))\r
1817         {\r
1818                 CRect thisrect, patchrect;\r
1819                 this->GetWindowRect(thisrect);\r
1820                 this->m_patchViewdlg.GetWindowRect(patchrect);\r
1821                 if(thisrect.right==patchrect.left)\r
1822                 {\r
1823                         int width = patchrect.Width();\r
1824                         patchrect.left -= (thisrect.right - pRect->right);\r
1825                         patchrect.right-= (thisrect.right - pRect->right);\r
1826 \r
1827                         if(     patchrect.bottom == thisrect.bottom)\r
1828                         {\r
1829                                 patchrect.bottom -= (thisrect.bottom - pRect->bottom);\r
1830                         }\r
1831                         if(     patchrect.top == thisrect.top)\r
1832                         {\r
1833                                 patchrect.top -=  thisrect.top-pRect->top;\r
1834                         }\r
1835                         m_patchViewdlg.MoveWindow(patchrect);\r
1836                 }\r
1837         }\r
1838         // TODO: Add your message handler code here\r
1839 }\r
1840 \r
1841 void CCommitDlg::OnHdnItemchangedFilelist(NMHDR *pNMHDR, LRESULT *pResult)\r
1842 {\r
1843         LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);\r
1844         // TODO: Add your control notification handler code here\r
1845         *pResult = 0;\r
1846         TRACE("Item Changed\r\n");\r
1847 }\r