OSDN Git Service

Fix issue #172: 'Compare Non-horizontally' doesn't support 3-way merge
[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
23 #include "stdafx.h"
24 #include "FileActionScript.h"
25 #include <vector>
26 #include "UnicodeString.h"
27 #include "Merge.h"
28 #include "OptionsDef.h"
29 #include "OptionsMgr.h"
30 #include "ShellFileOperations.h"
31 #include "paths.h"
32
33 using std::vector;
34
35 /**
36  * @brief Standard constructor.
37  */
38 FileActionScript::FileActionScript()
39 : m_bUseRecycleBin(true)
40 , m_bHasCopyOperations(false)
41 , m_bHasMoveOperations(false)
42 , m_bHasRenameOperations(false)
43 , m_bHasDelOperations(false)
44 , m_hParentWindow(nullptr)
45 , m_pCopyOperations(new ShellFileOperations())
46 , m_pMoveOperations(new ShellFileOperations())
47 , m_pRenameOperations(new ShellFileOperations())
48 , m_pDelOperations(new ShellFileOperations())
49 {
50 }
51
52 /**
53  * @brief Standard destructor.
54  */
55 FileActionScript::~FileActionScript()
56 {
57 }
58
59 /**
60  * @brief Remove last action item from the list.
61  * @return Item removed from the list.
62  */
63 FileActionItem FileActionScript::RemoveTailActionItem()
64 {
65         FileActionItem item = m_actions.back();
66         m_actions.pop_back();
67         return item;
68 }
69
70 /**
71  * @brief Create ShellFileOperations operation lists from our scripts.
72  *
73  * We use ShellFileOperations internally to do actual file operations.
74  * ShellFileOperations can do only one type of operation (copy, move, delete)
75  * with one instance at a time, so we use own instance for every
76  * type of action.
77  * @return One of CreateScriptReturn values.
78  */
79 int FileActionScript::CreateOperationsScripts()
80 {
81         UINT operation = 0;
82         FILEOP_FLAGS operFlags = 0;
83         bool bContinue = true;
84
85         // Copy operations first
86         operation = FO_COPY;
87         operFlags |= FOF_NOCONFIRMMKDIR | FOF_MULTIDESTFILES | FOF_NOCONFIRMATION;
88         if (m_bUseRecycleBin)
89                 operFlags |= FOF_ALLOWUNDO;
90
91         vector<FileActionItem>::const_iterator iter = m_actions.begin();
92         while (iter != m_actions.end() && bContinue)
93         {
94                 bool bSkip = false;
95                 if ((*iter).atype == FileAction::ACT_COPY && !(*iter).dirflag)
96                 {
97                         if (bContinue)
98                         {
99                                 if (!theApp.CreateBackup(true, (*iter).dest))
100                                 {
101                                         String strErr = _("Error backing up file");
102                                         AfxMessageBox(strErr.c_str(), MB_OK | MB_ICONERROR);
103                                         bContinue = false;
104                                 }
105                         }
106                 }
107
108                 if ((*iter).atype == FileAction::ACT_COPY &&
109                         !bSkip && bContinue)
110                 {
111                         m_pCopyOperations->AddSourceAndDestination((*iter).src, (*iter).dest);
112                         m_bHasCopyOperations = true;
113                 }
114                 ++iter;
115         }
116         if (!bContinue)
117         {
118                 m_bHasCopyOperations = false;
119                 m_pCopyOperations->Reset();
120                 return SCRIPT_USERCANCEL;
121         }
122         
123         if (m_bHasCopyOperations)
124                 m_pCopyOperations->SetOperation(operation, operFlags, m_hParentWindow);
125
126         // Move operations next
127         operation = FO_MOVE;
128         operFlags = FOF_MULTIDESTFILES;
129         if (m_bUseRecycleBin)
130                 operFlags |= FOF_ALLOWUNDO;
131
132         iter = m_actions.begin();
133         while (iter != m_actions.end())
134         {
135                 if ((*iter).atype == FileAction::ACT_MOVE)
136                 {
137                         m_pMoveOperations->AddSourceAndDestination((*iter).src, (*iter).dest);
138                         m_bHasMoveOperations = true;
139                 }
140                 ++iter;
141         }
142         if (m_bHasMoveOperations)
143                 m_pMoveOperations->SetOperation(operation, operFlags,  m_hParentWindow);
144
145         // Rename operations nextbbbb
146         operation = FO_RENAME;
147         operFlags = FOF_MULTIDESTFILES;
148         if (m_bUseRecycleBin)
149                 operFlags |= FOF_ALLOWUNDO;
150
151         iter = m_actions.begin();
152         while (iter != m_actions.end())
153         {
154                 if ((*iter).atype == FileAction::ACT_RENAME)
155                 {
156                         m_pRenameOperations->AddSourceAndDestination((*iter).src, (*iter).dest);
157                         m_bHasRenameOperations = true;
158                 }
159                 ++iter;
160         }
161         if (m_bHasRenameOperations)
162                 m_pRenameOperations->SetOperation(operation, operFlags, m_hParentWindow);
163
164         // Delete operations last
165         operation = FO_DELETE;
166         operFlags = 0;
167         if (m_bUseRecycleBin)
168                 operFlags |= FOF_ALLOWUNDO;
169
170         iter = m_actions.begin();
171         while (iter != m_actions.end())
172         {
173                 if ((*iter).atype == FileAction::ACT_DEL)
174                 {
175                         m_pDelOperations->AddSource((*iter).src);
176                         if (!(*iter).dest.empty())
177                                 m_pDelOperations->AddSource((*iter).dest);
178                         m_bHasDelOperations = true;
179                 }
180                 ++iter;
181         }
182         if (m_bHasDelOperations)
183                 m_pDelOperations->SetOperation(operation, operFlags, m_hParentWindow);
184         return SCRIPT_SUCCESS;
185 }
186
187 /**
188  * @brief Run one operation set.
189  * @param [in] oplist List of operations to run.
190  * @param [out] userCancelled Did user cancel the operation?
191  * @return true if the operation succeeded and finished.
192  */
193 bool FileActionScript::RunOp(ShellFileOperations *oplist, bool & userCancelled)
194 {
195         bool fileOpSucceed = false;
196         __try
197         {
198                 fileOpSucceed = oplist->Run();
199                 userCancelled = oplist->IsCanceled();
200         }
201         __except (EXCEPTION_EXECUTE_HANDLER)
202         {
203                 fileOpSucceed = false;
204         }
205         return fileOpSucceed;
206 }
207
208 /**
209  * @brief Execute fileoperations.
210  * @return `true` if all actions were done successfully, `false` otherwise.
211  */
212 bool FileActionScript::Run()
213 {
214         // Now process files/directories that got added to list
215         bool bFileOpSucceed = true;
216         bool bUserCancelled = false;
217         bool bRetVal = true;
218
219         CreateOperationsScripts();
220
221         if (m_bHasCopyOperations)
222         {
223                 vector<FileActionItem>::const_iterator iter = m_actions.begin();
224                 while (iter != m_actions.end())
225                 {
226                         if ((*iter).dirflag)
227                                 paths::CreateIfNeeded((*iter).dest);
228                         ++iter;
229                 }
230                 bFileOpSucceed = RunOp(m_pCopyOperations.get(), bUserCancelled);
231         }
232
233         if (m_bHasMoveOperations)
234         {
235                 if (bFileOpSucceed && !bUserCancelled)
236                 {
237                         bFileOpSucceed = RunOp(m_pMoveOperations.get(), bUserCancelled);
238                 }
239                 else
240                         bRetVal = false;
241         }
242
243         if (m_bHasRenameOperations)
244         {
245                 if (bFileOpSucceed && !bUserCancelled)
246                 {
247                         bFileOpSucceed = RunOp(m_pRenameOperations.get(), bUserCancelled);
248                 }
249                 else
250                         bRetVal = false;
251         }
252
253         if (m_bHasDelOperations)
254         {
255                 if (bFileOpSucceed && !bUserCancelled)
256                 {
257                         bFileOpSucceed = RunOp(m_pDelOperations.get(), bUserCancelled);
258                 }
259                 else
260                         bRetVal = false;
261         }
262
263         if (!bFileOpSucceed || bUserCancelled)
264                 bRetVal = false;
265
266         return bRetVal;
267 }