OSDN Git Service

Fixed issue #112: (7) Low hanging GUI fruits during Commit and Push
[tortoisegit/TortoiseGitJp.git] / src / TortoiseProc / RevertDlg.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 #include "stdafx.h"\r
20 #include "TortoiseProc.h"\r
21 #include "messagebox.h"\r
22 #include "Revertdlg.h"\r
23 #include "Git.h"\r
24 #include "Registry.h"\r
25 #include ".\revertdlg.h"\r
26 #include "CommonResource.h"\r
27 \r
28 #define REFRESHTIMER   100\r
29 \r
30 IMPLEMENT_DYNAMIC(CRevertDlg, CResizableStandAloneDialog)\r
31 CRevertDlg::CRevertDlg(CWnd* pParent /*=NULL*/)\r
32         : CResizableStandAloneDialog(CRevertDlg::IDD, pParent)\r
33         , m_bSelectAll(TRUE)\r
34         , m_bThreadRunning(FALSE)\r
35         , m_bCancelled(false)\r
36 {\r
37 }\r
38 \r
39 CRevertDlg::~CRevertDlg()\r
40 {\r
41 }\r
42 \r
43 void CRevertDlg::DoDataExchange(CDataExchange* pDX)\r
44 {\r
45         CResizableStandAloneDialog::DoDataExchange(pDX);\r
46         DDX_Control(pDX, IDC_REVERTLIST, m_RevertList);\r
47         DDX_Check(pDX, IDC_SELECTALL, m_bSelectAll);\r
48         DDX_Control(pDX, IDC_SELECTALL, m_SelectAll);\r
49 }\r
50 \r
51 \r
52 BEGIN_MESSAGE_MAP(CRevertDlg, CResizableStandAloneDialog)\r
53         ON_BN_CLICKED(IDHELP, OnBnClickedHelp)\r
54         ON_BN_CLICKED(IDC_SELECTALL, OnBnClickedSelectall)\r
55         ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_NEEDSREFRESH, OnSVNStatusListCtrlNeedsRefresh)\r
56         ON_REGISTERED_MESSAGE(CGitStatusListCtrl::SVNSLNM_ADDFILE, OnFileDropped)\r
57         ON_WM_TIMER()\r
58 END_MESSAGE_MAP()\r
59 \r
60 \r
61 \r
62 BOOL CRevertDlg::OnInitDialog()\r
63 {\r
64         CResizableStandAloneDialog::OnInitDialog();\r
65 \r
66         m_RevertList.Init(SVNSLC_COLTEXTSTATUS | SVNSLC_COLPROPSTATUS, _T("RevertDlg"));\r
67         m_RevertList.SetConfirmButton((CButton*)GetDlgItem(IDOK));\r
68         m_RevertList.SetSelectButton(&m_SelectAll);\r
69         m_RevertList.SetCancelBool(&m_bCancelled);\r
70         m_RevertList.SetBackgroundImage(IDI_REVERT_BKG);\r
71         m_RevertList.EnableFileDrop();\r
72 \r
73         GetWindowText(m_sWindowTitle);\r
74         \r
75         AdjustControlSize(IDC_SELECTALL);\r
76 \r
77         AddAnchor(IDC_REVERTLIST, TOP_LEFT, BOTTOM_RIGHT);\r
78         AddAnchor(IDC_SELECTALL, BOTTOM_LEFT);\r
79         AddAnchor(IDC_UNVERSIONEDITEMS, BOTTOM_RIGHT);\r
80         AddAnchor(IDOK, BOTTOM_RIGHT);\r
81         AddAnchor(IDCANCEL, BOTTOM_RIGHT);\r
82         AddAnchor(IDHELP, BOTTOM_RIGHT);\r
83         if (hWndExplorer)\r
84                 CenterWindow(CWnd::FromHandle(hWndExplorer));\r
85         EnableSaveRestore(_T("RevertDlg"));\r
86 \r
87         // first start a thread to obtain the file list with the status without\r
88         // blocking the dialog\r
89         if (AfxBeginThread(RevertThreadEntry, this)==0)\r
90         {\r
91                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
92         }\r
93         InterlockedExchange(&m_bThreadRunning, TRUE);\r
94 \r
95         return TRUE;\r
96 }\r
97 \r
98 UINT CRevertDlg::RevertThreadEntry(LPVOID pVoid)\r
99 {\r
100         return ((CRevertDlg*)pVoid)->RevertThread();\r
101 }\r
102 \r
103 UINT CRevertDlg::RevertThread()\r
104 {\r
105         // get the status of all selected file/folders recursively\r
106         // and show the ones which can be reverted to the user\r
107         // in a list control. \r
108         DialogEnableWindow(IDOK, false);\r
109         m_bCancelled = false;\r
110 \r
111         if (!m_RevertList.GetStatus(&m_pathList))\r
112         {\r
113                 m_RevertList.SetEmptyString(m_RevertList.GetLastErrorMessage());\r
114         }\r
115         m_RevertList.Show(SVNSLC_SHOWVERSIONEDBUTNORMALANDEXTERNALSFROMDIFFERENTREPOS | SVNSLC_SHOWDIRECTFILES | SVNSLC_SHOWEXTERNALFROMDIFFERENTREPO, \r
116                                                 // do not select all files, only the ones the user has selected directly\r
117                                                 SVNSLC_SHOWDIRECTFILES|SVNSLC_SHOWADDED);\r
118 \r
119         CTGitPath commonDir = m_RevertList.GetCommonDirectory(false);\r
120         SetWindowText(m_sWindowTitle + _T(" - ") + commonDir.GetWinPathString());\r
121 \r
122         if (m_RevertList.HasUnversionedItems())\r
123         {\r
124                 if (DWORD(CRegStdWORD(_T("Software\\TortoiseGit\\UnversionedAsModified"), FALSE)))\r
125                 {\r
126                         GetDlgItem(IDC_UNVERSIONEDITEMS)->ShowWindow(SW_SHOW);\r
127                 }\r
128                 else\r
129                         GetDlgItem(IDC_UNVERSIONEDITEMS)->ShowWindow(SW_HIDE);\r
130         }\r
131         else\r
132                 GetDlgItem(IDC_UNVERSIONEDITEMS)->ShowWindow(SW_HIDE);\r
133 \r
134         InterlockedExchange(&m_bThreadRunning, FALSE);\r
135         RefreshCursor();\r
136 \r
137         return 0;\r
138 }\r
139 \r
140 void CRevertDlg::OnOK()\r
141 {\r
142         if (m_bThreadRunning)\r
143                 return;\r
144         // save only the files the user has selected into the temporary file\r
145         m_bRecursive = TRUE;\r
146         for (int i=0; i<m_RevertList.GetItemCount(); ++i)\r
147         {\r
148                 if (!m_RevertList.GetCheck(i))\r
149                 {\r
150                         m_bRecursive = FALSE;\r
151                 }\r
152                 else \r
153                 {\r
154                         m_selectedPathList.AddPath(*(CTGitPath*)m_RevertList.GetItemData(i));\r
155 #if 0\r
156                         CGitStatusListCtrl::FileEntry * entry = m_RevertList.GetListEntry(i);\r
157                         // add all selected entries to the list, except the ones with 'added'\r
158                         // status: we later *delete* all the entries in the list before\r
159                         // the actual revert is done (so the user has the reverted files\r
160                         // still in the trash bin to recover from), but it's not good to\r
161                         // delete added files because they're not restored by the revert.\r
162                         if (entry->status != svn_wc_status_added)\r
163                                 m_selectedPathList.AddPath(entry->GetPath());\r
164                         // if an entry inside an external is selected, we can't revert\r
165                         // recursively anymore because the recursive revert stops at the\r
166                         // external boundaries.\r
167                         if (entry->IsInExternal())\r
168                                 m_bRecursive = FALSE;\r
169 #endif\r
170                 }\r
171         }\r
172         if (!m_bRecursive)\r
173         {\r
174                 m_RevertList.WriteCheckedNamesToPathList(m_pathList);\r
175         }\r
176         m_selectedPathList.SortByPathname();\r
177 \r
178         CResizableStandAloneDialog::OnOK();\r
179 }\r
180 \r
181 void CRevertDlg::OnCancel()\r
182 {\r
183         m_bCancelled = true;\r
184         if (m_bThreadRunning)\r
185                 return;\r
186 \r
187         CResizableStandAloneDialog::OnCancel();\r
188 }\r
189 \r
190 void CRevertDlg::OnBnClickedHelp()\r
191 {\r
192         OnHelp();\r
193 }\r
194 \r
195 void CRevertDlg::OnBnClickedSelectall()\r
196 {\r
197         UINT state = (m_SelectAll.GetState() & 0x0003);\r
198         if (state == BST_INDETERMINATE)\r
199         {\r
200                 // It is not at all useful to manually place the checkbox into the indeterminate state...\r
201                 // We will force this on to the unchecked state\r
202                 state = BST_UNCHECKED;\r
203                 m_SelectAll.SetCheck(state);\r
204         }\r
205         theApp.DoWaitCursor(1);\r
206         m_RevertList.SelectAll(state == BST_CHECKED);\r
207         theApp.DoWaitCursor(-1);\r
208 }\r
209 \r
210 BOOL CRevertDlg::PreTranslateMessage(MSG* pMsg)\r
211 {\r
212         if (pMsg->message == WM_KEYDOWN)\r
213         {\r
214                 switch (pMsg->wParam)\r
215                 {\r
216                 case VK_RETURN:\r
217                         {\r
218                                 if (GetAsyncKeyState(VK_CONTROL)&0x8000)\r
219                                 {\r
220                                         if ( GetDlgItem(IDOK)->IsWindowEnabled() )\r
221                                         {\r
222                                                 PostMessage(WM_COMMAND, IDOK);\r
223                                         }\r
224                                         return TRUE;\r
225                                 }\r
226                         }\r
227                         break;\r
228                 case VK_F5:\r
229                         {\r
230                                 if (!m_bThreadRunning)\r
231                                 {\r
232                                         if (AfxBeginThread(RevertThreadEntry, this)==0)\r
233                                         {\r
234                                                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
235                                         }\r
236                                         else\r
237                                                 InterlockedExchange(&m_bThreadRunning, TRUE);\r
238                                 }\r
239                         }\r
240                         break;\r
241                 }\r
242         }\r
243 \r
244         return CResizableStandAloneDialog::PreTranslateMessage(pMsg);\r
245 }\r
246 \r
247 LRESULT CRevertDlg::OnSVNStatusListCtrlNeedsRefresh(WPARAM, LPARAM)\r
248 {\r
249         if (AfxBeginThread(RevertThreadEntry, this)==0)\r
250         {\r
251                 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);\r
252         }\r
253         return 0;\r
254 }\r
255 \r
256 LRESULT CRevertDlg::OnFileDropped(WPARAM, LPARAM lParam)\r
257 {\r
258         BringWindowToTop();\r
259         SetForegroundWindow();\r
260         SetActiveWindow();\r
261         // if multiple files/folders are dropped\r
262         // this handler is called for every single item\r
263         // separately.\r
264         // To avoid creating multiple refresh threads and\r
265         // causing crashes, we only add the items to the\r
266         // list control and start a timer.\r
267         // When the timer expires, we start the refresh thread,\r
268         // but only if it isn't already running - otherwise we\r
269         // restart the timer.\r
270         CTGitPath path;\r
271         path.SetFromWin((LPCTSTR)lParam);\r
272 \r
273         if (!m_RevertList.HasPath(path))\r
274         {\r
275                 if (m_pathList.AreAllPathsFiles())\r
276                 {\r
277                         m_pathList.AddPath(path);\r
278                         m_pathList.RemoveDuplicates();\r
279                 }\r
280                 else\r
281                 {\r
282                         // if the path list contains folders, we have to check whether\r
283                         // our just (maybe) added path is a child of one of those. If it is\r
284                         // a child of a folder already in the list, we must not add it. Otherwise\r
285                         // that path could show up twice in the list.\r
286                         bool bHasParentInList = false;\r
287                         for (int i=0; i<m_pathList.GetCount(); ++i)\r
288                         {\r
289                                 if (m_pathList[i].IsAncestorOf(path))\r
290                                 {\r
291                                         bHasParentInList = true;\r
292                                         break;\r
293                                 }\r
294                         }\r
295                         if (!bHasParentInList)\r
296                         {\r
297                                 m_pathList.AddPath(path);\r
298                                 m_pathList.RemoveDuplicates();\r
299                         }\r
300                 }\r
301         }\r
302 \r
303         // Always start the timer, since the status of an existing item might have changed\r
304         SetTimer(REFRESHTIMER, 200, NULL);\r
305         ATLTRACE(_T("Item %s dropped, timer started\n"), path.GetWinPath());\r
306         return 0;\r
307 }\r
308 \r
309 void CRevertDlg::OnTimer(UINT_PTR nIDEvent)\r
310 {\r
311         switch (nIDEvent)\r
312         {\r
313         case REFRESHTIMER:\r
314                 if (m_bThreadRunning)\r
315                 {\r
316                         SetTimer(REFRESHTIMER, 200, NULL);\r
317                         ATLTRACE("Wait some more before refreshing\n");\r
318                 }\r
319                 else\r
320                 {\r
321                         KillTimer(REFRESHTIMER);\r
322                         ATLTRACE("Refreshing after items dropped\n");\r
323                         OnSVNStatusListCtrlNeedsRefresh(0, 0);\r
324                 }\r
325                 break;\r
326         }\r
327         __super::OnTimer(nIDEvent);\r
328 }\r