OSDN Git Service

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