This repository has been archived by the owner on Jul 20, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathNMSVRScreenshotFix.java
400 lines (356 loc) · 14.1 KB
/
NMSVRScreenshotFix.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
package nmsvrscreenshotfix;
import java.io.File;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import static java.lang.Character.isLetterOrDigit;
import java.io.IOException;
/**
* Program Description:
* Iterates through every image in the source folder and if applicable,
* based on the users selected settings, converts the files into the result folder.
* @author Noah Ortega
*/
public class NMSVRScreenshotFix {
/**
* Calls on the logic controller singleton.
*/
public static void main(String[] args) {
LogicController.getInstance();
}
}
/**
* Controller which handles any methods which handle program logic or state.
*/
class LogicController {
// State variables
private boolean isExecuting = false;
private boolean canceled = false;
private boolean triggerFileErrorPopups = true;
// File handling variables
private int totalFiles;
private int filesConverted;
private BufferedImage curImage;
// Path variables (changed via main ProgramUI)
public String sourcePath;
public String resultPath;
// Behavior Settings (changed via SettingsUI)
public boolean shouldRename = true;
public boolean renameNewFile = true;
public String addTextToFileName = "_fix";
public boolean addAsPrefix = false;
int MAX_ADD_TEXT_LENGTH = 50;
ProgramUI myUI;
/**
* Constructor: initiates paths to current directory calls to launch the UI
*/
private LogicController() {
sourcePath = System.getProperty("user.dir");
resultPath = System.getProperty("user.dir");
launchUI();
}
/**
* Singleton: ensures there is only one instance of this class.
*/
private static LogicController sharedController = null;
public static LogicController getInstance() {
if(sharedController == null) {
sharedController = new LogicController();
}
return sharedController;
}
/**
* Standard Java Swing launch, aesthetics, and beginning the UI thread
*/
private void launchUI() {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(ProgramUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(ProgramUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(ProgramUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(ProgramUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
myUI = new ProgramUI();
myUI.setVisible(true);
}
});
}
/**
* Iterates through files in the source directory, validates files before
* allowing resizing. Halts execution if necessary.
* @throws IOException if a file cannot be read from. skips, execution continues
* @throws NullPointerException if an image is not properly encoded as it's file type.
* Gives the user a warning that the file may be corrupted. skips, execution continues
*/
public void execute() {
System.out.println("Starting Execution");
isExecuting = true;
canceled = false;
myUI.updateProgressBar(0);
filesConverted = 0;
//folder of original screenshots
File sourceFolder = new File(sourcePath);
File[] folderContents = sourceFolder.listFiles();
totalFiles = folderContents.length;
File curFile;
//disables UI
myUI.toggleUI();
if(!shouldRename && (sourcePath.equals(resultPath))) {
myUI.warningReplacingFiles();
}
for (int fileIndex = 0; fileIndex < totalFiles && !canceled; fileIndex++) {
curFile = folderContents[fileIndex];
if (!folderContents[fileIndex].isDirectory() && isImage(curFile.getPath())) {
System.out.println(">file name: " + curFile.getName());
try {
curImage = ImageIO.read(folderContents[fileIndex]);
if (shouldResize(curImage.getWidth(), curImage.getHeight())) {
squish(curFile);
}
}
catch (IOException ex) {
if(triggerFileErrorPopups){
myUI.errorReading(curFile.getName());
}
System.err.println(">> Caught IOException on '" + curFile.getPath() + "':\n " + ex.getMessage());
}
catch (NullPointerException ex) {
if(triggerFileErrorPopups){
myUI.errorCorruptImage(curFile.getName());
}
System.out.println(">> ImageIO.read returned null (likely corrupt): " + ex);
}
}
myUI.updateProgressBar((fileIndex + 1)*100/totalFiles);
}
if(canceled) {
myUI.cancelPopup(filesConverted);
canceled = false;
myUI.updateProgressBar(0);
} else {
myUI.completePopup(filesConverted);
}
triggerFileErrorPopups = true;
isExecuting = false;
myUI.toggleUI();
System.out.println("Finished Execution");
}
/**
* Resizes an image to a 1:1 aspect ratio by changing the width
* @param originalFile Path of the original input image
* @throws IOException if a file cannot be written. gives user error. cancels execution.
*
* based on code by Nam Ha Minh from article "How to resize images in Java"
* https://www.codejava.net/java-se/graphics/how-to-resize-images-in-java
*/
private void squish(File originalFile) throws IOException {
int newWidth = curImage.getHeight();
int height = curImage.getHeight();
// creates output image
BufferedImage outputImage = new BufferedImage(newWidth, height, curImage.getType());
// scales the input image to the output image
Graphics2D g2d = outputImage.createGraphics();
g2d.drawImage(curImage, 0, 0, newWidth, height, null);
g2d.dispose();
int fileDotIndex = originalFile.getName().lastIndexOf('.');
String formatName = originalFile.getName().substring(fileDotIndex + 1);
File newFile = new File(resultPath +"/"+ originalFile.getName());
if(shouldRename) {
if (renameNewFile) {
newFile = modifyFilePath(resultPath,originalFile.getName());
}
else {
originalFile.renameTo(modifyFilePath(sourcePath,originalFile.getName()));
}
}
try {
// write to output file
ImageIO.write(outputImage, formatName, newFile);
filesConverted++;
System.out.println(">> Converted");
}
catch(Exception ex) {
if(triggerFileErrorPopups){
myUI.errorWriting(newFile.getName());
}
System.err.print(">> Error writing to result folder " + ex.getClass());
}
}
/**
* State variable setter, allows other classes to cancel execution
*/
public void cancelExecution() {
canceled = true;
}
/**
* Getter for state variable isExecuting
* @return true if the program is converting files.
*/
public boolean getIsExecuting() {
return isExecuting;
}
/**
* Generates a string which attempts to explain to the user the consequences
* of their behavior setting choices.
* @return String representing the current user selected settings
*/
public String getCurrentBehaviorString() {
String behavior = "";
if(!shouldRename && (sourcePath.equals(resultPath))) {
behavior += "• Replacing originals with converted screenshots";
}
else {
behavior += "• Making copies of converted screenshots";
}
behavior +="\n";
if(shouldRename) {
if(addAsPrefix) {
behavior += "• Adding prefix ";
}
else {
behavior += "• Adding suffix ";
}
behavior += "\""+addTextToFileName+"\" to ";
if(renameNewFile) {
behavior += "converted image";
}
else {
behavior += "original image";
}
behavior += "\n";
behavior += "• " + getExampleRename();
}
return behavior;
}
/**
* based on settings, adds a prefix or suffix onto the file name
* @param oldName the name of the original file before conversion
* @return the modified file name to be used for the new image
*/
private String getRename(String oldName) {
int dotIndex = oldName.lastIndexOf('.');
String name = oldName.substring(0, dotIndex);
String ext = oldName.substring(dotIndex);
return addAsPrefix ? (addTextToFileName+name+ext) : (name+addTextToFileName+ext);
}
/**
* Based on settings, creates an string showing an example renaming
* Helper function for {@link getCurrentBehaviorString}
* @return string explaining what the rename will look like
*/
private String getExampleRename() {
String exampleName = renameNewFile ? "converted.png" : "original.png";
return ("Ex: \"" + exampleName + "\" -> \"" + getRename(exampleName) +"\"");
}
/**
* Concatenates parent folder path with the file name
* @param parentPath path of the folder holding the file
* @param fileName the name of the file
* @return concatenated file path
*/
private File modifyFilePath(String parentPath, String fileName) {
return (new File(parentPath + "/" + getRename(fileName)));
}
/**
* Determines if a file is a basic image type
* @param path file path of image
* @return true if extension is png jpg or jpeg
*/
private boolean isImage(String path) {
//extention of file, from file after final period
int dotIndex = path.lastIndexOf('.');
String extension = (dotIndex == -1) ? "no extension" : path.substring(dotIndex + 1);
extension = extension.toLowerCase();
return (extension.equals("png") || extension.equals("jpg") || extension.equals("jpeg"));
}
/**
* Determines if an image should be resized based on criteria:
* - width must be greater than height
* @param width image width
* @param height image height
* @return true if image matches criteria
*/
private boolean shouldResize(int width, int height) {
if (width == height) {
System.out.println(">> Already 1:1 aspect ratio");
return false;
} else if (width < height) {
System.out.println(">> Height is greater than width");
return false;
} else {
return true;
}
}
/**
* For generating the path of the output image
* @param inputPath Path of the original input image
* @param dotIndex the index of the dot in the input path
* @return the final output path
*/
public String generateOutputPath(String inputPath, int dotIndex) {
return inputPath.substring(0, dotIndex) + addTextToFileName + inputPath.substring(dotIndex);
}
/**
* Validates text that will be added to file name. Triggers warning popups if false.
* @param phrase the text addition to be tested
* @return true if there is no problem with text
*/
public boolean isValidTextAddition(String phrase) {
if(phrase.length() == 0) {
myUI.warningEmptyText();
return false;
}
if(phrase.length() > MAX_ADD_TEXT_LENGTH) {
myUI.warningExceededTextLimit();
return false;
}
for (int charIndex = 0; charIndex < phrase.length(); charIndex++) {
char curChar = phrase.charAt(charIndex);
if(!isLetterOrDigit(curChar)) {
if(curChar != '_' && curChar != '-') {
myUI.warningInvalidText(curChar);
return false;
}
}
}
return true;
}
/**
* Verifies that source and result paths are existing directories
* @return true if source and result paths are valid
*/
public boolean hasValidDirectoryPaths() {
return((new File(sourcePath)).isDirectory() && (new File(resultPath)).isDirectory());
}
/**
* File errors during execution trigger warning popups in ProgramUI. The user makes a choice
* about how the error should be handled via this method.
* @see ProgramUI.fileErrorOptions for the text options these indexes correspond to
* @param userChoice the index of the choice the user selected
*/
public void userErrorResponse(int userChoice) {
if (userChoice == 1) {
triggerFileErrorPopups = false;
} else if (userChoice == 2) {
canceled = true;
}
}
}