OSDN Git Service

Merge with stable
[winmerge-jp/winmerge-jp.git] / Src / FileActionScript.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    License (GPLv2+):
3 //    This program is free software; you can redistribute it and/or modify
4 //    it under the terms of the GNU General Public License as published by
5 //    the Free Software Foundation; either version 2 of the License, or (at
6 //    your option) any later version.
7 //
8 //    This program is distributed in the hope that it will be useful, but
9 //    WITHOUT ANY WARRANTY; without even the implied warranty of
10 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //    GNU General Public License for more details.
12 //
13 //    You should have received a copy of the GNU General Public License
14 //    along with this program; if not, write to the Free Software
15 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 /////////////////////////////////////////////////////////////////////////////
17 /**
18  * @file  FileActionScript.cpp
19  *
20  * @brief Implementation of FileActionScript and related classes
21  */
22 // ID line follows -- this is updated by SVN
23 // $Id$
24
25 #include "stdafx.h"
26 #include "FileActionScript.h"
27 #include <vector>
28 #include "UnicodeString.h"
29 #include "Merge.h"
30 #include "OptionsDef.h"
31 #include "OptionsMgr.h"
32 #include "MainFrm.h"
33 #include "ShellFileOperations.h"
34 #include "paths.h"
35 #include "SourceControl.h"
36
37 using std::vector;
38
39 /**
40  * @brief Standard constructor.
41  */
42 FileActionScript::FileActionScript()
43 : m_bUseRecycleBin(TRUE)
44 , m_bHasCopyOperations(FALSE)
45 , m_bHasMoveOperations(FALSE)
46 , m_bHasRenameOperations(FALSE)
47 , m_bHasDelOperations(FALSE)
48 , m_hParentWindow(NULL)
49 , m_pCopyOperations(new ShellFileOperations())
50 , m_pMoveOperations(new ShellFileOperations())
51 , m_pRenameOperations(new ShellFileOperations())
52 , m_pDelOperations(new ShellFileOperations())
53 {
54 }
55
56 /**
57  * @brief Standard destructor.
58  */
59 FileActionScript::~FileActionScript()
60 {
61 }
62
63 /**
64  * @brief Remove last action item from the list.
65  * @return Item removed from the list.
66  */
67 FileActionItem FileActionScript::RemoveTailActionItem()
68 {
69         FileActionItem item = m_actions.back();
70         m_actions.pop_back();
71         return item;
72 }
73
74 /**
75  * @brief Set parent window used for showing MessageBoxes.
76  * @param [in] hWnd Handle to parent window.
77  */
78 void FileActionScript::SetParentWindow(HWND hWnd)
79 {
80         m_hParentWindow = hWnd;
81 }
82
83 /**
84  * @brief Does user want to move deleted files to Recycle Bin?
85  * @param [in] bUseRecycleBin If TRUE deleted files are moved to Recycle Bin.
86  */
87 void FileActionScript::UseRecycleBin(BOOL bUseRecycleBin)
88 {
89         m_bUseRecycleBin = bUseRecycleBin;
90 }
91
92 /**
93  * @brief Return amount of actions (copy, move, etc) in script.
94  * @return Amount of actions.
95  */
96 size_t FileActionScript::GetActionItemCount() const
97 {
98         return m_actions.size();
99 }
100
101 /**
102  * @brief Checkout file from VSS before synching (copying) it.
103  * @param [in] path Full path to a file.
104  * @param [in,out] bApplyToAll Apply user selection to all (selected)files?
105  * @return One of CreateScriptReturn values.
106  */
107 int FileActionScript::VCSCheckOut(const String &path, BOOL &bApplyToAll)
108 {
109         String strErr;
110         int retVal = SCRIPT_SUCCESS;
111
112         if (GetOptionsMgr()->GetInt(OPT_VCS_SYSTEM) == SourceControl::VCS_NONE)
113                 return retVal;
114
115         // TODO: First param is not used!
116         int nRetVal = theApp.SyncFileToVCS(path.c_str(), bApplyToAll, strErr);
117         if (nRetVal == -1)
118         {
119                 retVal = SCRIPT_FAIL; // So we exit without file operations done
120                 AfxMessageBox(strErr.c_str(), MB_OK | MB_ICONERROR);
121         }
122         else if (nRetVal == IDCANCEL)
123         {
124                 retVal = SCRIPT_USERCANCEL; // User canceled, so we don't continue
125         }
126         else if (nRetVal == IDNO)
127         {
128                 retVal = SCRIPT_USERSKIP;  // User wants to skip this item
129         }
130
131         return retVal;
132 }
133
134 /**
135  * @brief Create ShellFileOperations operation lists from our scripts.
136  *
137  * We use ShellFileOperations internally to do actual file operations.
138  * ShellFileOperations can do only one type of operation (copy, move, delete)
139  * with one instance at a time, so we use own instance for every
140  * type of action.
141  * @return One of CreateScriptReturn values.
142  */
143 int FileActionScript::CreateOperationsScripts()
144 {
145         UINT operation = 0;
146         FILEOP_FLAGS operFlags = 0;
147         BOOL bApplyToAll = FALSE;
148         BOOL bContinue = TRUE;
149
150         // Copy operations first
151         operation = FO_COPY;
152         operFlags |= FOF_NOCONFIRMMKDIR | FOF_MULTIDESTFILES | FOF_NOCONFIRMATION;
153         if (m_bUseRecycleBin)
154                 operFlags |= FOF_ALLOWUNDO;
155
156         vector<FileActionItem>::const_iterator iter = m_actions.begin();
157         while (iter != m_actions.end() && bContinue == TRUE)
158         {
159                 BOOL bSkip = FALSE;
160                 if ((*iter).atype == FileAction::ACT_COPY && !(*iter).dirflag)
161                 {
162                         // Handle VCS checkout
163                         // Before we can write over destination file, we must unlock
164                         // (checkout) it. This also notifies VCS system that the file
165                         // has been modified.
166                         if (GetOptionsMgr()->GetInt(OPT_VCS_SYSTEM) != SourceControl::VCS_NONE)
167                         {
168                                 int retVal = VCSCheckOut((*iter).dest, bApplyToAll);
169                                 if (retVal == SCRIPT_USERCANCEL)
170                                         bContinue = FALSE;
171                                 else if (retVal == SCRIPT_USERSKIP)
172                                         bSkip = TRUE;
173                                 else if (retVal == SCRIPT_FAIL)
174                                         bContinue = FALSE;
175                         }
176
177                         if (bContinue)
178                         {
179                                 if (!theApp.CreateBackup(TRUE, (*iter).dest.c_str()))
180                                 {
181                                         String strErr = _("Error backing up file");
182                                         AfxMessageBox(strErr.c_str(), MB_OK | MB_ICONERROR);
183                                         bContinue = FALSE;
184                                 }
185                         }
186                 }
187
188                 if ((*iter).atype == FileAction::ACT_COPY &&
189                         bSkip == FALSE && bContinue == TRUE)
190                 {
191                         m_pCopyOperations->AddSourceAndDestination((*iter).src, (*iter).dest);
192                         m_bHasCopyOperations = TRUE;
193                 }
194                 ++iter;
195         }
196         if (bContinue == FALSE)
197         {
198                 m_bHasCopyOperations = FALSE;
199                 m_pCopyOperations->Reset();
200                 return SCRIPT_USERCANCEL;
201         }
202         
203         if (m_bHasCopyOperations)
204                 m_pCopyOperations->SetOperation(operation, operFlags, m_hParentWindow);
205
206         // Move operations next
207         operation = FO_MOVE;
208         operFlags = FOF_MULTIDESTFILES;
209         if (m_bUseRecycleBin)
210                 operFlags |= FOF_ALLOWUNDO;
211
212         iter = m_actions.begin();
213         while (iter != m_actions.end())
214         {
215                 if ((*iter).atype == FileAction::ACT_MOVE)
216                 {
217                         m_pMoveOperations->AddSourceAndDestination((*iter).src, (*iter).dest);
218                         m_bHasMoveOperations = TRUE;
219                 }
220                 ++iter;
221         }
222         if (m_bHasMoveOperations)
223                 m_pMoveOperations->SetOperation(operation, operFlags,  m_hParentWindow);
224
225         // Rename operations nextbbbb
226         operation = FO_RENAME;
227         operFlags = FOF_MULTIDESTFILES;
228         if (m_bUseRecycleBin)
229                 operFlags |= FOF_ALLOWUNDO;
230
231         iter = m_actions.begin();
232         while (iter != m_actions.end())
233         {
234                 if ((*iter).atype == FileAction::ACT_RENAME)
235                 {
236                         m_pRenameOperations->AddSourceAndDestination((*iter).src, (*iter).dest);
237                         m_bHasRenameOperations = TRUE;
238                 }
239                 ++iter;
240         }
241         if (m_bHasRenameOperations)
242                 m_pRenameOperations->SetOperation(operation, operFlags, m_hParentWindow);
243
244         // Delete operations last
245         operation = FO_DELETE;
246         operFlags = 0;
247         if (m_bUseRecycleBin)
248                 operFlags |= FOF_ALLOWUNDO;
249
250         iter = m_actions.begin();
251         while (iter != m_actions.end())
252         {
253                 if ((*iter).atype == FileAction::ACT_DEL)
254                 {
255                         m_pDelOperations->AddSource((*iter).src);
256                         if (!(*iter).dest.empty())
257                                 m_pDelOperations->AddSource((*iter).dest);
258                         m_bHasDelOperations = TRUE;
259                 }
260                 ++iter;
261         }
262         if (m_bHasDelOperations)
263                 m_pDelOperations->SetOperation(operation, operFlags, m_hParentWindow);
264         return SCRIPT_SUCCESS;
265 }
266
267 /**
268  * @brief Run one operation set.
269  * @param [in] oplist List of operations to run.
270  * @param [out] userCancelled Did user cancel the operation?
271  * @return true if the operation succeeded and finished.
272  */
273 bool FileActionScript::RunOp(ShellFileOperations *oplist, bool & userCancelled)
274 {
275         bool fileOpSucceed = false;
276         __try
277         {
278                 fileOpSucceed = oplist->Run();
279                 userCancelled = oplist->IsCanceled();
280         }
281         __except (EXCEPTION_EXECUTE_HANDLER)
282         {
283                 fileOpSucceed = false;
284         }
285         return fileOpSucceed;
286 }
287
288 /**
289  * @brief Execute fileoperations.
290  * @return TRUE if all actions were done successfully, FALSE otherwise.
291  */
292 BOOL FileActionScript::Run()
293 {
294         // Now process files/directories that got added to list
295         bool bFileOpSucceed = true;
296         bool bUserCancelled = false;
297         BOOL bRetVal = TRUE;
298
299         CreateOperationsScripts();
300
301         if (m_bHasCopyOperations)
302         {
303                 vector<FileActionItem>::const_iterator iter = m_actions.begin();
304                 while (iter != m_actions.end())
305                 {
306                         if ((*iter).dirflag)
307                                 paths_CreateIfNeeded((*iter).dest);
308                         ++iter;
309                 }
310                 bFileOpSucceed = RunOp(m_pCopyOperations.get(), bUserCancelled);
311         }
312
313         if (m_bHasMoveOperations)
314         {
315                 if (bFileOpSucceed && !bUserCancelled)
316                 {
317                         bFileOpSucceed = RunOp(m_pMoveOperations.get(), bUserCancelled);
318                 }
319                 else
320                         bRetVal = FALSE;
321         }
322
323         if (m_bHasRenameOperations)
324         {
325                 if (bFileOpSucceed && !bUserCancelled)
326                 {
327                         bFileOpSucceed = RunOp(m_pRenameOperations.get(), bUserCancelled);
328                 }
329                 else
330                         bRetVal = FALSE;
331         }
332
333         if (m_bHasDelOperations)
334         {
335                 if (bFileOpSucceed && !bUserCancelled)
336                 {
337                         bFileOpSucceed = RunOp(m_pDelOperations.get(), bUserCancelled);
338                 }
339                 else
340                         bRetVal = FALSE;
341         }
342
343         if (!bFileOpSucceed || bUserCancelled)
344                 bRetVal = FALSE;
345
346         return bRetVal;
347 }