Complete file finder and updates for presets

This commit is contained in:
Melon 2022-05-10 15:09:18 +01:00
parent ee3bc43fb4
commit b8edecb7bb
Signed by: melon
GPG Key ID: B0ADD5395BCDAAB6
12 changed files with 389 additions and 64 deletions

15
pom.xml
View File

@ -58,6 +58,21 @@
<artifactId>flatlaf</artifactId> <artifactId>flatlaf</artifactId>
<version>2.2</version> <version>2.2</version>
</dependency> </dependency>
<dependency>
<groupId>com.weblookandfeel</groupId>
<artifactId>weblaf-core</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>com.weblookandfeel</groupId>
<artifactId>weblaf-ui</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.19</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>

View File

@ -0,0 +1,181 @@
package net.mightypork.utils;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
// https://stackoverflow.com/a/18004334/10719432
public class DesktopApi {
public static boolean browse(URI uri) {
if (openSystemSpecific(uri.toString())) return true;
return browseDESKTOP(uri);
}
public static boolean open(File file) {
if (openSystemSpecific(file.getPath())) return true;
return openDESKTOP(file);
}
public static boolean edit(File file) {
if (openSystemSpecific(file.getPath())) return true;
return editDESKTOP(file);
}
private static boolean openSystemSpecific(String what) {
EnumOS os = getOs();
if (os.isLinux()) {
if (runCommand("kde-open", what)) return true;
if (runCommand("gnome-open", what)) return true;
if (runCommand("xdg-open", what)) return true;
}
if (os.isMac()) {
if (runCommand("open", what)) return true;
}
if (os.isWindows()) {
return runCommand("explorer", what);
}
return false;
}
private static boolean browseDESKTOP(URI uri) {
logOut("Trying to use Desktop.getDesktop().browse() with " + uri.toString());
try {
if (!Desktop.isDesktopSupported()) {
logErr("Platform is not supported.");
return false;
}
if (!Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
logErr("BROWSE is not supported.");
return false;
}
Desktop.getDesktop().browse(uri);
return true;
} catch (Throwable t) {
logErr("Error using desktop browse.", t);
return false;
}
}
private static boolean openDESKTOP(File file) {
logOut("Trying to use Desktop.getDesktop().open() with " + file.toString());
try {
if (!Desktop.isDesktopSupported()) {
logErr("Platform is not supported.");
return false;
}
if (!Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
logErr("OPEN is not supported.");
return false;
}
Desktop.getDesktop().open(file);
return true;
} catch (Throwable t) {
logErr("Error using desktop open.", t);
return false;
}
}
private static boolean editDESKTOP(File file) {
logOut("Trying to use Desktop.getDesktop().edit() with " + file);
try {
if (!Desktop.isDesktopSupported()) {
logErr("Platform is not supported.");
return false;
}
if (!Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) {
logErr("EDIT is not supported.");
return false;
}
Desktop.getDesktop().edit(file);
return true;
} catch (Throwable t) {
logErr("Error using desktop edit.", t);
return false;
}
}
private static boolean runCommand(String command, String file) {
String[] parts = prepareCommand(command, file);
try {
Process p = Runtime.getRuntime().exec(parts);
if (p == null) return false;
try {
int result = p.exitValue();
if (result == 0) logErr("Process ended immediately.");
else logErr("Process crashed.");
return false;
} catch (IllegalThreadStateException ignored) {
return true;
}
} catch (IOException e) {
return false;
}
}
private static String[] prepareCommand(String command, String file) {
List<String> parts = new ArrayList<>();
parts.add(command);
for (String s : "%s".split(" ")) {
s = String.format(s, file);
parts.add(s.trim());
}
return parts.toArray(new String[0]);
}
private static void logErr(String msg, Throwable t) {
System.err.println(msg);
t.printStackTrace();
}
private static void logErr(String msg) {
System.err.println(msg);
}
private static void logOut(String msg) {
System.out.println(msg);
}
public enum EnumOS {
Linux, MacOS, Solaris, Unknown, Windows;
public boolean isLinux() {
return this == Linux || this == Solaris;
}
public boolean isMac() {
return this == MacOS;
}
public boolean isWindows() {
return this == Windows;
}
}
public static EnumOS getOs() {
String s = System.getProperty("os.name").toLowerCase();
if (s.contains("win")) return EnumOS.Windows;
if (s.contains("mac")) return EnumOS.MacOS;
if (s.contains("solaris")) return EnumOS.Solaris;
if (s.contains("sunos")) return EnumOS.Solaris;
if (s.contains("linux")) return EnumOS.Linux;
if (s.contains("unix")) return EnumOS.Linux;
return EnumOS.Unknown;
}
}

View File

@ -16,6 +16,7 @@ import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class CodeSize extends JFrame { public class CodeSize extends JFrame {
@ -23,14 +24,15 @@ public class CodeSize extends JFrame {
private JTextField pathField; private JTextField pathField;
private JTextField regexField; private JTextField regexField;
private InfoPanel infoPanel; private InfoPanel infoPanel;
private JButton searchButton;
public CodeSize(Settings settings) { public CodeSize(Settings settings) {
super("Code Size"); super("Code Size");
this.settings = settings; this.settings = settings;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(600, 600)); setPreferredSize(new Dimension(800, 600));
setMinimumSize(new Dimension(600, 600)); setMinimumSize(new Dimension(800, 600));
setContentPane(createMainPanel()); setContentPane(createMainPanel());
setJMenuBar(createMenuBar()); setJMenuBar(createMenuBar());
} }
@ -38,13 +40,13 @@ public class CodeSize extends JFrame {
private JPanel createMainPanel() { private JPanel createMainPanel() {
pathField = ComponentUtils.fixedTextFieldHeight(new JTextField()); pathField = ComponentUtils.fixedTextFieldHeight(new JTextField());
regexField = ComponentUtils.fixedTextFieldHeight(new JTextField()); regexField = ComponentUtils.fixedTextFieldHeight(new JTextField());
infoPanel = new InfoPanel(); infoPanel = new InfoPanel(this);
JButton browseButton = new JButton("Browse"); JButton browseButton = new JButton("Browse");
browseButton.addActionListener(e -> browseForFolder()); browseButton.addActionListener(e -> browseForFolder());
JButton presetButton = new JButton("Preset"); JButton presetButton = new JButton("Preset");
presetButton.addActionListener(e -> presetForRegex()); presetButton.addActionListener(e -> presetForRegex());
JButton searchButton = new JButton("Search"); searchButton = new JButton("Search");
searchButton.addActionListener(e -> searchCodeFiles()); searchButton.addActionListener(e -> searchCodeFiles());
Box pathRow = Box.createHorizontalBox(); Box pathRow = Box.createHorizontalBox();
@ -157,12 +159,20 @@ public class CodeSize extends JFrame {
return; return;
} }
Pattern pattern; Pattern pattern;
String rawPattern = regexField.getText();
if (Objects.equals(rawPattern, "")) rawPattern = "\\..+$";
try { try {
pattern = Pattern.compile(regexField.getText()); pattern = Pattern.compile(rawPattern);
} catch (Exception ex) { } catch (Exception ex) {
JOptionPane.showMessageDialog(this, "Failed to parse regex pattern", "Code Size", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(this, "Failed to parse regex pattern", "Code Size", JOptionPane.ERROR_MESSAGE);
return; return;
} }
new Thread(() -> FileFinder.search(mainFile, pattern, infoPanel)).start(); searchButton.setEnabled(false);
infoPanel.reset();
new Thread(() -> {
FileFinder.search(mainFile, pattern, infoPanel);
infoPanel.finishLoading();
SwingUtilities.invokeLater(() -> searchButton.setEnabled(true));
}).start();
} }
} }

View File

@ -1,13 +1,22 @@
package xyz.mrmelon54.codesize.components; package xyz.mrmelon54.codesize.components;
import net.mightypork.utils.DesktopApi;
import xyz.mrmelon54.codesize.CodeSize;
import xyz.mrmelon54.codesize.loading.LoadingAnimation;
import xyz.mrmelon54.codesize.utils.ByteUtils; import xyz.mrmelon54.codesize.utils.ByteUtils;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class InfoPanel extends Box { public class InfoPanel extends JPanel {
private final DefaultListModel<String> fileListModel = new DefaultListModel<>(); private final DefaultListModel<InfoPanelFile> fileListModel = new DefaultListModel<>();
private final List<InfoPanelFile> fileListObjects = new ArrayList<>();
public InfoPanelItem totalFiles = new InfoPanelItem("Total Files", false); public InfoPanelItem totalFiles = new InfoPanelItem("Total Files", false);
public InfoPanelItem totalSize = new InfoPanelItem("Total Size", true); public InfoPanelItem totalSize = new InfoPanelItem("Total Size", true);
public InfoPanelItem averageSize = new InfoPanelItem("Average Size", true); public InfoPanelItem averageSize = new InfoPanelItem("Average Size", true);
@ -18,14 +27,35 @@ public class InfoPanel extends Box {
public InfoPanelItem maxLines = new InfoPanelItem("Max Lines", false); public InfoPanelItem maxLines = new InfoPanelItem("Max Lines", false);
public InfoPanelItem minLines = new InfoPanelItem("Min Lines", false); public InfoPanelItem minLines = new InfoPanelItem("Min Lines", false);
private final List<InfoPanelItem> values = new ArrayList<>(); private final List<InfoPanelItem> values = new ArrayList<>();
private final LoadingAnimation loadingAnimation = new LoadingAnimation();
private final Timer loadingTimer;
private long loadingStartTime;
public InfoPanel() { public InfoPanel(CodeSize codeSize) {
super(BoxLayout.Y_AXIS); super();
setLayout(new BorderLayout());
JList<String> fileListInfo = new JList<>(); JList<InfoPanelFile> fileListInfo = new JList<>();
fileListInfo.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); fileListInfo.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
fileListInfo.setModel(fileListModel); fileListInfo.setModel(fileListModel);
add(fileListInfo); fileListInfo.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
InfoPanelFile selectedValue = fileListInfo.getSelectedValue();
if (selectedValue == null) return;
File f = selectedValue.file();
if (DesktopApi.edit(f)) {
System.out.println("Opened file: '" + f.getAbsolutePath() + "'");
} else {
String s = "Failed to open file '%s' in default text editor".formatted(f.getAbsolutePath());
System.out.println(s);
JOptionPane.showMessageDialog(codeSize, s, "Code Size", JOptionPane.ERROR_MESSAGE);
}
} else super.mouseClicked(e);
}
});
add(new JScrollPane(fileListInfo), BorderLayout.CENTER);
values.add(totalFiles); values.add(totalFiles);
values.add(totalSize); values.add(totalSize);
@ -36,15 +66,59 @@ public class InfoPanel extends Box {
values.add(averageLines); values.add(averageLines);
values.add(maxLines); values.add(maxLines);
values.add(minLines); values.add(minLines);
values.forEach(infoPanelItem -> add(infoPanelItem.component));
Box box = Box.createVerticalBox();
box.setBorder(new EmptyBorder(0, 8, 0, 0));
values.forEach(infoPanelItem -> box.add(infoPanelItem.component));
add(box, BorderLayout.EAST);
loadingTimer = new Timer(1, e -> repaint());
}
@Override
public void paint(Graphics g) {
super.paint(g);
if (loadingTimer.isRunning()) {
int w = getWidth();
int h = getHeight();
g.setColor(new Color(0f, 0f, 0f, .5f));
g.fillRect(0, 0, w, h);
loadingAnimation.paint(g.create(w / 2 - 100, h / 2 - 100, 200, 200));
}
}
public void reset() {
fileListObjects.clear();
fileListModel.clear();
values.forEach(infoPanelItem -> infoPanelItem.setValue(0));
loadingStartTime = System.currentTimeMillis();
loadingAnimation.reset();
loadingTimer.start();
}
public void finishLoading() {
long l = System.currentTimeMillis() - loadingStartTime;
if (l < 2000) {
try {
Thread.sleep(2000 - l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
loadingTimer.stop();
repaint();
} }
public void updateValues() { public void updateValues() {
values.forEach(InfoPanelItem::updateValue); SwingUtilities.invokeLater(() -> {
fileListModel.clear();
fileListObjects.forEach(fileListModel::addElement);
values.forEach(InfoPanelItem::updateValue);
});
} }
public void addFileOutput(String s) { public void addFileOutput(InfoPanelFile infoPanelFile) {
fileListModel.addElement(s); fileListObjects.add(infoPanelFile);
} }
public static class InfoPanelItem { public static class InfoPanelItem {
@ -61,14 +135,21 @@ public class InfoPanel extends Box {
updateValue(); updateValue();
} }
// only use inside invokeLater() call
private void updateValue() { private void updateValue() {
String v = byteUnits ? ByteUtils.ToBytesCount((long) value) : String.valueOf(value); String v = byteUnits ? ByteUtils.ToBytesCount((long) value) : String.valueOf(value);
SwingUtilities.invokeLater(() -> component.setText(label + ": " + v)); component.setText(label + ": " + v);
} }
public void setValue(float v) { public void setValue(float v) {
value = v; value = v;
updateValue(); }
}
public record InfoPanelFile(File file, long lines, long size) {
@Override
public String toString() {
return ByteUtils.ToBytesCount(size) + " -- " + lines + " -- " + file.getName();
} }
} }
} }

View File

@ -0,0 +1,37 @@
package xyz.mrmelon54.codesize.loading;
import java.awt.*;
public class LoadingAnimation {
private long lastTime = 0;
private float tick;
private Color[] ovalColors = new Color[]{
new Color(0x0099e5),
new Color(0xff4c4c),
new Color(0x34bf49),
new Color(0xedea42),
};
public void reset() {
lastTime = System.nanoTime();
tick = 0;
}
public void paint(Graphics g) {
double deltaTime = (lastTime - (lastTime = System.nanoTime())) / -150000000f;
tick += deltaTime;
for (int i = 0; i < 8; i++) {
g.setColor(getOvalColor(i));
g.fillOval(25 * i + 2, getOvalY(tick + i * 0.7f), 21, 21);
}
}
private Color getOvalColor(int i) {
return ovalColors[i % ovalColors.length];
}
private int getOvalY(float y) {
return (int) (Math.sin(y) * 50 + 100 - 21 / 2f);
}
}

View File

@ -8,8 +8,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static xyz.mrmelon54.codesize.utils.ByteUtils.ToBytesCount;
public class FileFinder { public class FileFinder {
public static void search(File mainFile, Pattern pattern, InfoPanel infoPanel) { public static void search(File mainFile, Pattern pattern, InfoPanel infoPanel) {
List<File> searchable = new ArrayList<>(); List<File> searchable = new ArrayList<>();
@ -29,11 +27,10 @@ public class FileFinder {
if (files1 == null) continue; if (files1 == null) continue;
Collections.addAll(searchable, files1); Collections.addAll(searchable, files1);
File[] files2 = path1.listFiles((dir, name) -> { File[] files2 = path1.listFiles((dir, name) -> pattern.matcher(name).find());
return pattern.matcher(name).find();
});
if (files2 == null) return; if (files2 == null) return;
for (File file : files2) { for (File file : files2) {
if (!file.isFile()) continue;
long len = file.length(); long len = file.length();
totalFiles++; totalFiles++;
totalSize += len; totalSize += len;
@ -43,7 +40,7 @@ public class FileFinder {
totalLines += lineCount; totalLines += lineCount;
if (lineCount > maxLines) maxLines = lineCount; if (lineCount > maxLines) maxLines = lineCount;
if (lineCount < minLines || minLines == 0) minLines = lineCount; if (lineCount < minLines || minLines == 0) minLines = lineCount;
infoPanel.addFileOutput(ToBytesCount(len) + " -- " + lineCount + " -- " + file.getName()); infoPanel.addFileOutput(new InfoPanel.InfoPanelFile(file, lineCount, len));
} }
infoPanel.totalFiles.setValue(totalFiles); infoPanel.totalFiles.setValue(totalFiles);

View File

@ -45,6 +45,10 @@ public class PresetSelector extends JDialog {
copyButton.addActionListener(this::copyAction); copyButton.addActionListener(this::copyAction);
JIconButton editButton = new JIconButton(GoogleMaterialDesignIcons.EDIT, 16, 24); JIconButton editButton = new JIconButton(GoogleMaterialDesignIcons.EDIT, 16, 24);
editButton.addActionListener(this::editAction); editButton.addActionListener(this::editAction);
JIconButton upButton = new JIconButton(GoogleMaterialDesignIcons.KEYBOARD_ARROW_UP, 16, 24);
upButton.addActionListener(this::upAction);
JIconButton downButton = new JIconButton(GoogleMaterialDesignIcons.KEYBOARD_ARROW_DOWN, 16, 24);
downButton.addActionListener(this::downAction);
JButton chooseButton = new JButton("Choose"); JButton chooseButton = new JButton("Choose");
chooseButton.addActionListener(this::selectAction); chooseButton.addActionListener(this::selectAction);
@ -56,6 +60,8 @@ public class PresetSelector extends JDialog {
hBox.add(removeButton); hBox.add(removeButton);
hBox.add(editButton); hBox.add(editButton);
hBox.add(copyButton); hBox.add(copyButton);
hBox.add(upButton);
hBox.add(downButton);
hBox.add(Box.createHorizontalGlue()); hBox.add(Box.createHorizontalGlue());
hBox.add(chooseButton); hBox.add(chooseButton);
hBox.add(cancelButton); hBox.add(cancelButton);
@ -147,4 +153,26 @@ public class PresetSelector extends JDialog {
private void cancelAction(ActionEvent actionEvent) { private void cancelAction(ActionEvent actionEvent) {
dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
} }
private void upAction(ActionEvent actionEvent) {
Pair<Integer, Preset> selectedPreset = getSelectedPreset();
int idx = selectedPreset.t();
int prev = idx - 1;
if (prev < 0) return;
codeSize.settings.presets.setElementAt(codeSize.settings.presets.elementAt(prev), idx);
codeSize.settings.presets.setElementAt(selectedPreset.u(), prev);
reload();
presetList.setSelectedIndex(prev);
}
private void downAction(ActionEvent actionEvent) {
Pair<Integer, Preset> selectedPreset = getSelectedPreset();
int idx = selectedPreset.t();
int next = idx + 1;
if (next >= codeSize.settings.presets.size()) return;
codeSize.settings.presets.setElementAt(codeSize.settings.presets.elementAt(next), idx);
codeSize.settings.presets.setElementAt(selectedPreset.u(), next);
reload();
presetList.setSelectedIndex(next);
}
} }

View File

@ -1,36 +0,0 @@
package xyz.mrmelon54.codesize.utils;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.function.IntFunction;
public record ArrayUtils<T>(IntFunction<T[]> generator) {
public T[] concat(T[] a, T[] b) {
if (a == null && b == null) return null;
if (a == null) return b;
if (b == null) return a;
final int aLen = Array.getLength(a);
final int bLen = Array.getLength(b);
if (aLen == 0) return b;
if (bLen == 0) return a;
T[] result = generator.apply(aLen + bLen);
System.arraycopy(a, 0, result, 0, aLen);
System.arraycopy(b, 0, result, aLen, bLen);
return result;
}
public T[] append(T[] a, T b) {
if (a == null) {
T[] result = generator.apply(1);
result[0] = b;
return result;
}
final int aLen = Array.getLength(a);
T[] result = generator.apply(aLen + 1);
System.arraycopy(a, 0, result, 0, aLen);
result[aLen] = b;
return result;
}
}

View File

@ -7,8 +7,8 @@ public class ByteUtils {
if ((len < unit)) return String.format("%d %s", len, unitStr); if ((len < unit)) return String.format("%d %s", len, unitStr);
else unitStr = unitStr.toUpperCase(); else unitStr = unitStr.toUpperCase();
byte[] a = "KMGTPEZY".getBytes(); String a = "KMGTPEZY";
int exp = ((int) ((Math.log(len) / Math.log(unit)))); int exp = ((int) ((Math.log(len) / Math.log(unit))));
return String.format("%.2f %s%s", (len / Math.pow(unit, exp)), a[(exp - 1)], unitStr); return String.format("%.2f %s%s", (len / Math.pow(unit, exp)), a.charAt(exp - 1), unitStr);
} }
} }

View File

@ -5,7 +5,7 @@ import java.awt.*;
public class ComponentUtils { public class ComponentUtils {
public static JTextField fixedTextFieldHeight(JTextField textField) { public static JTextField fixedTextFieldHeight(JTextField textField) {
textField.setMaximumSize(new Dimension(textField.getMaximumSize().width, textField.getPreferredSize().height)); textField.setMaximumSize(new Dimension(textField.getMaximumSize().width, textField.getPreferredSize().height + 10));
return textField; return textField;
} }
} }

View File

@ -12,7 +12,7 @@ public class Preset {
public String regex; public String regex;
public boolean builtin; public boolean builtin;
private static final Pattern rawNamePattern = Pattern.compile("(.+) \\(Copy \\d+\\)"); // TODO private static final Pattern rawNamePattern = Pattern.compile("(.+) \\(Copy \\d+\\)");
@Override @Override
public String toString() { public String toString() {

View File

@ -11,6 +11,12 @@
"regex": "\\.(java)$", "regex": "\\.(java)$",
"builtin": true "builtin": true
}, },
{
"uuid": "2ca15e4f-dff8-4da9-8408-52d884f729ec",
"name": "Go Project",
"regex": "\\.(go)$",
"builtin": true
},
{ {
"uuid": "1b71b0a9-365d-45ff-8159-4a1acd869be0", "uuid": "1b71b0a9-365d-45ff-8159-4a1acd869be0",
"name": "Node Project", "name": "Node Project",
@ -28,5 +34,11 @@
"name": "Website Project", "name": "Website Project",
"regex": "\\.(html|js|css|php)$", "regex": "\\.(html|js|css|php)$",
"builtin": true "builtin": true
},
{
"uuid": "0a721596-7ae9-435b-9ad2-3c6fce84853d",
"name": "C/C++ Project",
"regex": "\\.(c|cpp|c\\+\\+|h)$",
"builtin": true
} }
] ]