-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathSnippetCreator.java
424 lines (380 loc) · 12.2 KB
/
SnippetCreator.java
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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/*
* IJ BAR: https://github.com/tferr/Scripts
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation (http://www.gnu.org/licenses/gpl.txt).
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*/
package bar.plugin;
import java.awt.AWTEvent;
import java.awt.Button;
import java.awt.Choice;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.scijava.Context;
import org.scijava.command.CommandService;
import bar.Installer;
import bar.Runner;
import bar.Utils;
import bar.gui.GuiUtils;
import ij.IJ;
import ij.gui.DialogListener;
import ij.gui.GenericDialog;
import ij.gui.MultiLineLabel;
import ij.gui.NonBlockingGenericDialog;
import ij.gui.YesNoCancelDialog;
import ij.plugin.PlugIn;
/**
* Generates templates for new IJ scripts, implementing the
* {@literal BAR> Utilities> New Routine...} command.
*/
public class SnippetCreator implements PlugIn, DialogListener, ActionListener {
/* Definitions */
private static final String[] S_TYPES = { "[Select from list]",
"BeanShell", "Clojure", "Groovy", "IJ1 Macro", "JavaScript",
"Python", "Ruby" };
private static final String[] S_EXTS = { "", ".bsh", ".clj", ".groovy",
".ijm", ".js", ".py", ".rb" };
private static final String[] C_CHARS = { "", "//", ";", "//", "//", "//",
"#", "#" };
private static final int NN = 0;
private static final int BSH = 1;
private static final int CLJ = 2;
private static final int GRV = 3;
private static final int IJM = 4;
private static final int JS = 5;
private static final int PY = 6;
private static final int RB = 7;
/* Default parameters for new snippet */
private String sContents = "";
private String sFilename = "My_Routine";
private int sType = 0;
private GenericDialog gd;
private Button paste, load, list;
private MultiLineLabel infoMsg;
private TextArea ta;
private static Runner runner;
public SnippetCreator() {
if (runner==null)
runner = new Runner(true);
}
/**
* This method is called when the plugin is loaded. See
* {@link ij.plugin.PlugIn#run(java.lang.String)} Prompts user for a new
* snippet that will be saved in {@code BAR/My_Routines/}
*
* @param arg
* ignored (Otherwise specified in plugins.config).
*/
@Override
public void run(final String arg) {
Utils.shiftClickWarning();
if (new GuiUtils().getFileCount(Utils.getLibDir()) == 0) {
final YesNoCancelDialog query = new YesNoCancelDialog(null, "Install lib Files?",
"Some of the code generated by this plugin assumes the adoption\n"
+ "of centralized lib files, but none seem to exist in your local directory\n" + "("
+ Utils.getLibDir() + ")\nWould you like to install them now?");
if (query.cancelPressed()) {
return;
} else if (query.yesPressed()) {
final Context context = (Context) IJ.runPlugIn("org.scijava.Context", "");
final CommandService commandService = context.getService(CommandService.class);
commandService.run(Installer.class, true);
return;
}
}
if (showDialog()) {
if (sContents.length()>0)
saveAndOpenSnippet();
else
IJ.showStatus(sFilename +" was empty. No file was saved...");
}
}
/* Returns a header common to all file types */
private static StringBuilder commonHeader(final StringBuilder sb, final int type) {
final Date date = new Date();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
sb.append(C_CHARS[type]).append(" File created on ")
.append(sdf.format(date)).append(", IJ ")
.append(IJ.getFullVersion()).append("\n");
switch (type) {
case NN:
case IJM:
break;
default:
sb.append(C_CHARS[type])
.append(" See 'BAR>Help>' for scripting resources\n\n");
break;
}
return sb;
}
/* Returns a header common to all file types */
private static StringBuilder commonHeader(final int type) {
final StringBuilder sb = new StringBuilder();
return commonHeader(sb, type);
}
private static String header(final String boilerplateName, final int lang) {
final String boilerplate = runner.readContents("boilerplate/" + boilerplateName);
if (boilerplate == null)
return commonHeader(lang).toString();
final String placeholder = C_CHARS[lang] +"header\n";
if (boilerplate.indexOf(placeholder) == -1)
return boilerplate;
final String header = commonHeader(lang).toString();
return boilerplate.replace(placeholder, header);
}
/**
* Returns the header for a BSH snippet (BeanShell).
*
* @return the lines of code required to load the BeanShell BAR library
*/
public static String bshHeader() {
return header("beanshell.bsh", BSH);
}
/**
* Returns the header for a CLJ snippet (Clojure).
*
* @return the lines of code required to load the Clojure BAR library
*/
public static String cljHeader() {
return header("clojure.clj", CLJ);
}
/**
* Returns the header for a GRV snippet (Groovy).
*
* @return the lines of code required to load the Groovy BAR library
*/
public static String grvHeader() {
return header("groovy.groovy", GRV);
}
/**
* Returns the header for a IJM snippet (IJ1 macro).
*
* @return the lines of code required to load the IJM BAR library
*/
public static String ijmHeader() {
return header("ijmlanguage.ijm", IJM);
}
/**
* Returns the header for a JS snippet (JavaScript).
*
* @return the lines of code required to load the JavaScript BAR library
*/
public static String jsHeader() {
return header("javascript.js", JS);
}
/**
* Returns the header for a PY snippet (Python).
*
* @return the lines of code required to load the Python BAR library
*/
public static String pyHeader() {
return header("python.py", PY);
}
/**
* Returns the header for a RB snippet (Ruby).
*
* @return the lines of code required to load the Ruby BAR library
*/
public static String rbHeader() {
return header("ruby.rb", RB);
}
/**
* Returns the header for a snippet of unknown language
*
* @return the placeholder string common to all headers
*/
public static String nnHeader() {
final StringBuilder sb = commonHeader(NN);
return sb.toString();
}
/* Saves and opens the snippet created by the prompt */
private void saveAndOpenSnippet() {
final String result = IJ.saveString(sContents, Utils.getMyRoutinesDir()
+ sFilename);
if (result == null) {
Utils.openScript(Utils.getMyRoutinesDir(), sFilename);
} else if (IJ.showMessageWithCancel("Snippet Creator",
"An error has occurred while saving the file.\n"
+ "Display snippet in the Log window?")) {
IJ.log("\n*** " + sFilename + " contents ***\n" + sContents);
}
}
/* Matches leading dot, ":", slashes, etc. in filename */
private boolean invalidFilename(final String filename) {
final String FILE_PATTERN = "^\\.|[/:\\\\]";
final Pattern pattern = Pattern.compile(FILE_PATTERN);
final Matcher matcher = pattern.matcher(filename);
return matcher.find();
}
/* Displays prompt */
private boolean showDialog() {
gd = new NonBlockingGenericDialog("New Snippet");
addButtons(gd);
gd.setInsets(0, 0, 0);
gd.addTextAreas(sContents, null, 20, 80);
ta = gd.getTextArea1();
ta.setFont(new Font("Monospaced", Font.PLAIN, 12));
gd.addStringField("Filename:", sFilename, 16);
gd.addChoice("Language:", S_TYPES, S_TYPES[sType]);
gd.setInsets(-85, 245, 0);
gd.addMessage(" \n "); // placeholder for info messages
gd.addHelp(Utils.getDocURL());
gd.setHelpLabel("Online Help");
gd.setOKLabel(" Create and Open ");
gd.addDialogListener(this);
gd.showDialog();
sContents = gd.getNextText();
return !gd.wasCanceled();
}
/*
* Retrieves parameters from prompt in an interactive way
*
* @see ij.gui.DialogListener#dialogItemChanged(ij.gui.GenericDialog,
* java.awt.AWTEvent)
*/
@Override
public boolean dialogItemChanged(final GenericDialog gd, final AWTEvent e) {
final Object source = (e == null) ? null : e.getSource();
final Vector<?> choices = gd.getChoices();
final Vector<?> fields = gd.getStringFields();
final Button[] buttons = gd.getButtons();
final Choice fChoice = (Choice) choices.elementAt(0);
final TextField fField = (TextField) fields.elementAt(0);
final Button okButton = buttons[0];
sFilename = gd.getNextString();
sType = gd.getNextChoiceIndex();
infoMsg = (MultiLineLabel) gd.getMessage();
// Populate text area
if (source == fChoice) {
String header = "";
switch (sType) {
case BSH:
header = bshHeader();
break;
case CLJ:
header = cljHeader();
break;
case GRV:
header = grvHeader();
break;
case IJM:
header = ijmHeader();
break;
case JS:
header = jsHeader();
break;
case PY:
header = pyHeader();
break;
case RB:
header = rbHeader();
break;
}
if (header != "")
appendToTextArea(header);
// Ensure adequate filename extension
if (!sFilename.endsWith(S_EXTS[sType])) {
final int index = sFilename.lastIndexOf(".");
if (index > -1)
sFilename = sFilename.substring(0, index);
sFilename += S_EXTS[sType];
fField.setText(sFilename);
}
}
// Adjust labels and fields
final File f = new File(Utils.getMyRoutinesDir() + sFilename);
final boolean invalidName = invalidFilename(sFilename);
okButton.setLabel(f.exists() ? "Replace and Open" : " Create and Open ");
fField.setForeground((f.exists()||invalidName) ? Color.RED : Color.BLACK);
// Adjust messages
final StringBuilder sb = new StringBuilder();
if (invalidName) {
sb.append("\nInvalid Filename");
} else if (f.exists()) {
sb.append("File already exists in BAR/My_Routines!");
} else if (sFilename.indexOf("_") == -1) {
sb.append("\nFile does not contain an underscore");
sb.append("\nand will not be listed in the BAR Menu.");
} else {
sb.append("\nFile will be listed in the BAR Menu.");
}
infoMsg.setText(sb.toString());
infoMsg.setForeground(Color.DARK_GRAY);
return !invalidName;
}
/* Adds custom buttons to prompt */
private void addButtons(final GenericDialog gd) {
final Panel p = new Panel();
p.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
paste = new Button("Append clipboard text");
paste.addActionListener(this);
p.add(paste);
load = new Button("Append file contents...");
load.addActionListener(this);
p.add(load);
list = new Button("List routines");
list.addActionListener(this);
p.add(list);
gd.addPanel(p, GridBagConstraints.CENTER, new Insets(0, 0, 0, 0));
}
/*
* Defines actions for custom buttons
*
* @see
* java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(final ActionEvent e) {
final Object source = e.getSource();
if (source==list) {
final java.awt.Point pos = list.getLocationOnScreen();
final int xPos = (int) pos.getX() + 10;
final int yPos = (int) pos.getY() + 10;
Utils.listDirectory(Utils.getMyRoutinesDir(), xPos, yPos);
// gd.toFront();
} else if (source==paste) {
final String clipboard = Utils.getClipboardText();
if (clipboard!=null)
appendToTextArea(clipboard);
else
infoMsg.setText("\nNo valid text in clipboard.");
} else if (source==load) {
final String file = IJ.openAsString("");
if (file==null) return;
if (file.startsWith("Error: "))
IJ.error("Snippet Creator", file.substring(7));
else {
if (file.length()>30000)
IJ.error("Snippet Creator", "File is too large");
else
appendToTextArea(file);
}
}
}
/* Inserts text at the active position of the TextArea of the main dialog */
private void appendToTextArea(String string) {
final int startPos = ta.getCaretPosition();
if (!string.endsWith("\n")) string +="\n";
ta.insert(string, startPos);
ta.select(startPos, ta.getCaretPosition());
ta.requestFocusInWindow();
}
}