forked from gdevic/GitForce
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ClassTool.cs
276 lines (244 loc) · 10.1 KB
/
ClassTool.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Xml;
namespace GitForce
{
/// <summary>
/// Class describing a custom tool.
/// Also contains functions to Load, Save and Run custom tools.
/// </summary>
public class ClassTool : ICloneable
{
public string Name;
public string Cmd;
public string Args;
public string Dir;
public string Desc;
public bool[] Checks = new bool[7];
/// <summary>
/// Implements the clonable interface.
/// </summary>
public object Clone()
{
return MemberwiseClone();
}
// A set of access functions to return a specific boolean tool's settings
public bool IsAddToContextMenu() { return Checks[0]; }
private bool IsConsoleApp() { return Checks[1]; }
private bool IsWriteOutput() { return Checks[2]; }
private bool IsCloseWindowOnExit() { return Checks[3]; }
private bool IsRefresh() { return Checks[4]; }
private bool IsPromptForArgs() { return Checks[5]; }
private bool IsAddBrowse() { return Checks[6]; }
/// <summary>
/// ToString override dumps all tool information for debug.
/// </summary>
public override string ToString()
{
return String.Format("Name: {0}\nCmd: {1}\nArgs: {2}\nDir: {3}\n",
Name, Cmd, Args, Dir);
}
/// <summary>
/// Runs a custom tool.
/// Returns a string with a tool output to be printed out.
/// This string can be empty, in which case nothing should be printed.
/// </summary>
public string Run(List<string> files)
{
App.PrintLogMessage(ToString());
string stdout = string.Empty;
string args = DeMacroise(Args, files);
// Add custom arguments if the checkbox to Prompt for Arguments was checked
if (IsPromptForArgs())
{
// Description is used as a question for the arguments, shown in the window title bar
string desc = Name;
if (!string.IsNullOrEmpty(Desc)) desc += ": " + Desc;
FormCustomToolArgs formCustomToolArgs = new FormCustomToolArgs(desc, args, IsAddBrowse());
if (formCustomToolArgs.ShowDialog() == DialogResult.Cancel)
return string.Empty;
args = formCustomToolArgs.GetArgs();
}
App.StatusBusy(true);
// Prepare the process to be run
Process proc = new Process();
proc.StartInfo.FileName = Cmd;
proc.StartInfo.Arguments = args;
proc.StartInfo.WorkingDirectory = DeMacroise(Dir, new List<string>());
proc.StartInfo.UseShellExecute = false;
try
{
// Run the custom tool in two ways (console app and GUI app)
if (IsConsoleApp())
{
// Start a console process
proc.StartInfo.CreateNoWindow = false;
// If we have to keep the window open (CMD/SHELL) after exit,
// we start the command line app in a different way, using a
// shell command (in which case we cannot redirect the stdout)
if (IsCloseWindowOnExit())
{
App.MainForm.SetTitle("Waiting for " + Cmd + " to finish...");
// Redirect standard output to our status pane if requested
if (IsWriteOutput())
{
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.OutputDataReceived += ProcOutputDataReceived;
proc.ErrorDataReceived += ProcOutputDataReceived;
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
else
{
proc.Start();
proc.WaitForExit();
}
}
else
{
// We need to keep the CMD/SHELL window open, so start the process using
// the CMD/SHELL as the root process and pass it our command to execute
proc.StartInfo.Arguments = string.Format("{0} {1} {2}",
ClassUtils.GetShellExecFlags(), proc.StartInfo.FileName, proc.StartInfo.Arguments);
proc.StartInfo.FileName = ClassUtils.GetShellExecCmd();
App.PrintLogMessage(proc.StartInfo.Arguments);
proc.Start();
}
}
else
{
// Start a GUI process
proc.StartInfo.CreateNoWindow = true;
// We can start the process and wait for it to finish only if we need to
// refresh the app after the process has exited.
proc.Start();
}
if (IsRefresh())
{
App.MainForm.SetTitle("Waiting for " + Cmd + " to finish...");
proc.WaitForExit();
App.DoRefresh();
}
}
catch (Exception ex)
{
App.PrintStatusMessage(ex.Message);
MessageBox.Show(ex.Message, "Error executing custom tool", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
proc.Close();
App.StatusBusy(false);
return stdout;
}
/// <summary>
/// Callback that handles process printing to stdout
/// Print to the application status pane.
/// </summary>
private void ProcOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (String.IsNullOrEmpty(e.Data)) return;
App.PrintStatusMessage(e.Data + Environment.NewLine);
}
/// <summary>
/// Applies a set of macro resolutions to the input string
/// </summary>
private string DeMacroise(string s, List<string> files)
{
// Without the current repo, we cannot have reasonable macro expansions
s = s.Replace("%r", App.Repos.Current==null ? "" : App.Repos.Current.Root);
s = s.Replace("%u", App.Repos.Current==null ? "" : App.Repos.Current.UserName);
s = s.Replace("%e", App.Repos.Current==null ? "" : App.Repos.Current.UserEmail);
s = s.Replace("%b", App.Repos.Current==null ? "" : App.Repos.Current.Branches.Current);
// Separate given list into list of files and list of directories
List<string> F = new List<string>();
List<string> D = new List<string>();
foreach (string f in files)
{
if (Directory.Exists(f))
// For directories, remove trailing slash
D.Add(f.TrimEnd(new[] {'\\', '/'}));
else
F.Add(f);
}
// Single file and single directory
string sf = F.Count > 0 ? F[0] : "";
string sd = D.Count > 0 ? D[0] : "";
s = s.Replace("%f", sf);
s = s.Replace("%d", sd);
s = s.Replace("%F", string.Join(" ", F.ToArray()));
s = s.Replace("%D", string.Join(" ", D.ToArray()));
return s;
}
}
/// <summary>
/// Class describing our set of custom tools
/// </summary>
public class ClassCustomTools
{
/// <summary>
/// List of tools
/// </summary>
public readonly List<ClassTool> Tools = new List<ClassTool>();
/// <summary>
/// Load a set of tools from a given file into the current tool-set.
/// Returns a new class structure containing all the tools if the tools loaded correctly.
/// If load failed, return empty class and print the error message to a main pane.
/// </summary>
public static ClassCustomTools Load(string name)
{
ClassCustomTools ct = new ClassCustomTools();
try
{
XmlSerializer deserializer = new XmlSerializer(typeof(ClassCustomTools));
using (TextReader textReader = new StreamReader(name))
{
ct = (ClassCustomTools)deserializer.Deserialize(textReader);
}
}
catch (Exception ex)
{
App.PrintStatusMessage("Error loading custom tools: " + ex.Message);
}
return ct;
}
/// <summary>
/// Save current set of tools to a given file.
/// Returns true if save successful.
/// If save failed, return false and print the error message to a main pane.
/// </summary>
public bool Save(string name)
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(ClassCustomTools));
using (TextWriter textWriter = new StreamWriter(name))
{
serializer.Serialize(textWriter, this);
}
}
catch (Exception ex)
{
App.PrintStatusMessage("Error saving custom tools: " + ex.Message);
return false;
}
return true;
}
/// <summary>
/// Implements a deep copy of the whole class.
/// </summary>
public ClassCustomTools Copy()
{
ClassCustomTools ct = new ClassCustomTools();
foreach (var classTool in Tools)
ct.Tools.Add((ClassTool)classTool.Clone());
return ct;
}
}
}