OSDN Git Service

Fix crash when input character at filter box
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / CreatePatchDlg.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // Copyright (C) 2003-2008 - TortoiseSVN\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 \r
20 #include "stdafx.h"\r
21 #include "TortoiseProc.h"\r
22 #include "MessageBox.h"\r
23 #include "CreatePatchDlg.h"\r
24 #include "SVN.h"\r
25 \r
26 #define REFRESHTIMER   100\r
27 \r
28 IMPLEMENT_DYNAMIC(CCreatePatch, CResizableStandAloneDialog)\r
29 CCreatePatch::CCreatePatch(CWnd* pParent /*=NULL*/)\r
30         : CResizableStandAloneDialog(CCreatePatch::IDD, pParent)\r
31         , m_bThreadRunning(FALSE)\r
32         , m_bCancelled(false)\r
33         , m_bShowUnversioned(FALSE)\r
34 {\r
35 }\r
36 \r
37 CCreatePatch::~CCreatePatch()\r
38 {\r
39 }\r
40 \r
41 void CCreatePatch::DoDataExchange(CDataExchange* pDX)\r
42 {\r
43         CResizableStandAloneDialog::DoDataExchange(pDX);\r
44         DDX_Control(pDX, IDC_PATCHLIST, m_PatchList);\r
45         DDX_Control(pDX, IDC_SELECTALL, m_SelectAll);\r
46         DDX_Check(pDX, IDC_SHOWUNVERSIONED, m_bShowUnversioned);\r
47 }\r
48 \r
49 \r
50 BEGIN_MESSAGE_MAP(CCreatePatch, CResizableStandAloneDialog)\r
51         ON_BN_CLICKED(IDC_SELECTALL, OnBnClickedSelectall)\r
52         ON_BN_CLICKED(IDC_SHOWUNVERSIONED, OnBnClickedShowunversioned)\r
53         ON_BN_CLICKED(IDHELP, OnBnClickedHelp)\r
54         ON_REGISTERED_MESSAGE(CSVNStatusListCtrl::SVNSLNM_NEEDSREFRESH, OnSVNStatusListCtrlNeedsRefresh)\r
55         ON_REGISTERED_MESSAGE(CSVNStatusListCtrl::SVNSLNM_ADDFILE, OnFileDropped)\r
56         ON_WM_TIMER()\r
57 END_MESSAGE_MAP()\r
58 \r
59 BOOL CCreatePatch::OnInitDialog()\r
60 {\r
61         CResizableStandAloneDialog::OnInitDialog();\r
62 \r
63         UpdateData(FALSE);\r
64 \r
65         m_PatchList.Init(0, _T("CreatePatchDlg"), SVNSLC_POPALL ^ (SVNSLC_POPIGNORE|SVNSLC_POPCOMMIT));\r
66         m_PatchList.SetConfirmButton((CButton*)GetDlgItem(IDOK));\r
67         m_PatchList.SetSelectButton(&m_SelectAll);\r
68         m_PatchList.SetCancelBool(&m_bCancelled);\r
69         m_PatchList.EnableFileDrop();\r
70 \r
71         AdjustControlSize(IDC_SELECTALL);\r
72         AdjustControlSize(IDC_SHOWUNVERSIONED);\r
73 \r
74         AddAnchor(IDC_PATCHLIST, TOP_LEFT, BOTTOM_RIGHT);\r
75         AddAnchor(IDC_SELECTALL, BOTTOM_LEFT);\r
76         AddAnchor(IDC_SHOWUNVERSIONED, BOTTOM_LEFT);\r
77         AddAnchor(IDOK, BOTTOM_RIGHT);\r
78         AddAnchor(IDCANCEL, BOTTOM_RIGHT);\r
79         AddAnchor(IDHELP, BOTTOM_RIGHT);\r
80         if (hWndExplorer)\r
81                 CenterWindow(CWnd::FromHandle(hWndExplorer));\r
82         EnableSaveRestore(_T("CreatePatchDlg"));\r
83 \r
84         // first start a thread to obtain the file list with the status without\r
85         // blocking the dialog\r
86         if(AfxBeginThread(PatchThreadEntry, this) == NULL)\r
87         {\r
88                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
89         }\r
90         InterlockedExchange(&m_bThreadRunning, TRUE);\r
91 \r
92         return TRUE;\r
93 }\r
94 \r
95 UINT CCreatePatch::PatchThreadEntry(LPVOID pVoid)\r
96 {\r
97         return ((CCreatePatch*)pVoid)->PatchThread();\r
98 }\r
99 \r
100 DWORD CCreatePatch::ShowMask()\r
101 {\r
102         return\r
103                 SVNSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS | SVNSLC_SHOWDIRECTFILES |\r
104                 (m_bShowUnversioned ? SVNSLC_SHOWUNVERSIONED : 0); \r
105 }\r
106 \r
107 UINT CCreatePatch::PatchThread()\r
108 {\r
109         // get the status of all selected file/folders recursively\r
110         // and show the ones which can be included in a patch (i.e. the versioned and not-normal ones)\r
111         DialogEnableWindow(IDOK, false);\r
112         DialogEnableWindow(IDC_SHOWUNVERSIONED, false);\r
113         m_bCancelled = false;\r
114 \r
115         if (!m_PatchList.GetStatus(m_pathList))\r
116         {\r
117                 m_PatchList.SetEmptyString(m_PatchList.GetLastErrorMessage());\r
118         }\r
119 \r
120         m_PatchList.Show(\r
121                 ShowMask(),     SVNSLC_SHOWDIRECTFILES | SVNSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS);\r
122 \r
123         DialogEnableWindow(IDC_SHOWUNVERSIONED, true);\r
124         InterlockedExchange(&m_bThreadRunning, FALSE);\r
125         return 0;\r
126 }\r
127 \r
128 BOOL CCreatePatch::PreTranslateMessage(MSG* pMsg)\r
129 {\r
130         if (pMsg->message == WM_KEYDOWN)\r
131         {\r
132                 switch (pMsg->wParam)\r
133                 {\r
134                 case VK_RETURN:\r
135                         {\r
136                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
137                                 {\r
138                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
139                                         {\r
140                                                 PostMessage(WM_COMMAND, IDOK);\r
141                                         }\r
142                                         return TRUE;\r
143                                 }\r
144                         }\r
145                         break;\r
146                 case VK_F5:\r
147                         {\r
148                                 if (!m_bThreadRunning)\r
149                                 {\r
150                                         if(AfxBeginThread(PatchThreadEntry, this) == NULL)\r
151                                         {\r
152                                                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
153                                         }\r
154                                         else\r
155                                                 InterlockedExchange(&m_bThreadRunning, TRUE);\r
156                                 }\r
157                         }\r
158                         break;\r
159                 }\r
160         }\r
161 \r
162         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
163 }\r
164 \r
165 void CCreatePatch::OnBnClickedSelectall()\r
166 {\r
167         UINT state = (m_SelectAll.GetState() & 0x0003);\r
168         if (state == BST_INDETERMINATE)\r
169         {\r
170                 // It is not at all useful to manually place the checkbox into the indeterminate state...\r
171                 // We will force this on to the unchecked state\r
172                 state = BST_UNCHECKED;\r
173                 m_SelectAll.SetCheck(state);\r
174         }\r
175         theApp.DoWaitCursor(1);\r
176         m_PatchList.SelectAll(state == BST_CHECKED);\r
177         theApp.DoWaitCursor(-1);\r
178 }\r
179 \r
180 void CCreatePatch::OnBnClickedShowunversioned()\r
181 {\r
182         UpdateData();\r
183         if (!m_bThreadRunning)\r
184                 m_PatchList.Show(ShowMask());\r
185 }\r
186 \r
187 void CCreatePatch::OnBnClickedHelp()\r
188 {\r
189         OnHelp();\r
190 }\r
191 \r
192 void CCreatePatch::OnCancel()\r
193 {\r
194         m_bCancelled = true;\r
195         if (m_bThreadRunning)\r
196                 return;\r
197 \r
198         CResizableStandAloneDialog::OnCancel();\r
199 }\r
200 \r
201 void CCreatePatch::OnOK()\r
202 {\r
203         if (m_bThreadRunning)\r
204                 return;\r
205 \r
206         int nListItems = m_PatchList.GetItemCount();\r
207         m_filesToRevert.Clear();\r
208         \r
209         for (int j=0; j<nListItems; j++)\r
210         {\r
211                 const CSVNStatusListCtrl::FileEntry * entry = m_PatchList.GetListEntry(j);\r
212                 if (entry->IsChecked())\r
213                 {\r
214                         // Unversioned files are not included in the resulting patch file!\r
215                         // We add those files to a list which will be used to add those files\r
216                         // before creating the patch.\r
217                         if ((entry->status == svn_wc_status_none)||(entry->status == svn_wc_status_unversioned))\r
218                         {\r
219                                 m_filesToRevert.AddPath(entry->GetPath());\r
220                         }\r
221                 }\r
222         }\r
223 \r
224         if (m_filesToRevert.GetCount())\r
225         {\r
226                 // add all unversioned files to version control\r
227                 // so they're included in the resulting patch\r
228                 // NOTE: these files must be reverted after the patch\r
229                 // has been created! Since this dialog doesn't create the patch\r
230                 // itself, the calling function is responsible to revert these files!\r
231                 SVN svn;\r
232                 svn.Add(m_filesToRevert, NULL, svn_depth_empty, false, false, true);\r
233         }\r
234         \r
235         //save only the files the user has selected into the path list\r
236         m_PatchList.WriteCheckedNamesToPathList(m_pathList);\r
237 \r
238         CResizableStandAloneDialog::OnOK();\r
239 }\r
240 \r
241 LRESULT CCreatePatch::OnSVNStatusListCtrlNeedsRefresh(WPARAM, LPARAM)\r
242 {\r
243         if(AfxBeginThread(PatchThreadEntry, this) == NULL)\r
244         {\r
245                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
246         }\r
247         return 0;\r
248 }\r
249 \r
250 LRESULT CCreatePatch::OnFileDropped(WPARAM, LPARAM lParam)\r
251 {\r
252         BringWindowToTop();\r
253         SetForegroundWindow();\r
254         SetActiveWindow();\r
255         // if multiple files/folders are dropped\r
256         // this handler is called for every single item\r
257         // separately.\r
258         // To avoid creating multiple refresh threads and\r
259         // causing crashes, we only add the items to the\r
260         // list control and start a timer.\r
261         // When the timer expires, we start the refresh thread,\r
262         // but only if it isn't already running - otherwise we\r
263         // restart the timer.\r
264         CTSVNPath path;\r
265         path.SetFromWin((LPCTSTR)lParam);\r
266 \r
267         if (!m_PatchList.HasPath(path))\r
268         {\r
269                 if (m_pathList.AreAllPathsFiles())\r
270                 {\r
271                         m_pathList.AddPath(path);\r
272                         m_pathList.RemoveDuplicates();\r
273                 }\r
274                 else\r
275                 {\r
276                         // if the path list contains folders, we have to check whether\r
277                         // our just (maybe) added path is a child of one of those. If it is\r
278                         // a child of a folder already in the list, we must not add it. Otherwise\r
279                         // that path could show up twice in the list.\r
280                         bool bHasParentInList = false;\r
281                         for (int i=0; i<m_pathList.GetCount(); ++i)\r
282                         {\r
283                                 if (m_pathList[i].IsAncestorOf(path))\r
284                                 {\r
285                                         bHasParentInList = true;\r
286                                         break;\r
287                                 }\r
288                         }\r
289                         if (!bHasParentInList)\r
290                         {\r
291                                 m_pathList.AddPath(path);\r
292                                 m_pathList.RemoveDuplicates();\r
293                         }\r
294                 }\r
295         }\r
296 \r
297         // Always start the timer, since the status of an existing item might have changed\r
298         SetTimer(REFRESHTIMER, 200, NULL);\r
299         ATLTRACE(_T("Item %s dropped, timer started\n"), path.GetWinPath());\r
300         return 0;\r
301 }\r
302 \r
303 void CCreatePatch::OnTimer(UINT_PTR nIDEvent)\r
304 {\r
305         switch (nIDEvent)\r
306         {\r
307         case REFRESHTIMER:\r
308                 if (m_bThreadRunning)\r
309                 {\r
310                         SetTimer(REFRESHTIMER, 200, NULL);\r
311                         ATLTRACE("Wait some more before refreshing\n");\r
312                 }\r
313                 else\r
314                 {\r
315                         KillTimer(REFRESHTIMER);\r
316                         ATLTRACE("Refreshing after items dropped\n");\r
317                         OnSVNStatusListCtrlNeedsRefresh(0, 0);\r
318                 }\r
319                 break;\r
320         }\r
321         __super::OnTimer(nIDEvent);\r
322 }\r