import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultHighlighter; import javax.swing.text.Highlighter; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.io.*; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; public class Init { final private Properties settings; final private File settingsFile = new File("settings.properties"); public Init() { settings = new Properties(); loadSettings(); JFrame frame = new JFrame("Formatted Data Viewer"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(800, 600); JMenuBar menuBar = new JMenuBar(); JMenu settingsMenu = new JMenu("Settings"); JMenuItem textColorItem = new JMenuItem("Change Text Color"); JMenuItem backgroundColorItem = new JMenuItem("Change Background Color"); JMenuItem highlightColorItem = new JMenuItem("Change Highlight Color"); settingsMenu.add(textColorItem); settingsMenu.add(backgroundColorItem); settingsMenu.add(highlightColorItem); menuBar.add(settingsMenu); frame.setJMenuBar(menuBar); JTextArea textArea = new JTextArea(); textArea.setEditable(true); textArea.setFocusable(true); textArea.setForeground(Color.decode(settings.getProperty("textColor", "FFFFFF"))); textArea.setBackground(Color.decode(settings.getProperty("bgColor", "FFFFFF"))); Color hcolor = Color.decode(settings.getProperty("highlightColor")); frame.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if(e.isControlDown() && e.getKeyCode() == 70){ searchbox(frame,textArea,hcolor); textArea.requestFocusInWindow(); } } }); textArea.setFont(new java.awt.Font("Monospaced", java.awt.Font.PLAIN, 20)); // Monospaced for alignment textArea.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if(e.isControlDown() && e.getKeyCode() == 70){ searchbox(frame,textArea,hcolor); textArea.requestFocusInWindow(); } } }); JScrollPane scrollPane = new JScrollPane(textArea); frame.add(scrollPane); frame.setVisible(true); textColorItem.addActionListener(e -> changeTextColor(textArea)); backgroundColorItem.addActionListener(e -> changeColor(textArea)); highlightColorItem.addActionListener(e -> changeHighlightColor(textArea)); loadFileAndDisplay(textArea); } public void changeTextColor(JTextArea textArea){ Color newColor = JColorChooser.showDialog(new JFrame(), "Choose Text Color", Color.LIGHT_GRAY); if (newColor != null) { settings.setProperty("textColor", "#" + Integer.toHexString(newColor.getRGB()).substring(2)); textArea.setForeground(newColor); saveSettings(); } } public void changeHighlightColor(JTextArea textArea){ Color newColor = JColorChooser.showDialog(new JFrame(), "Choose Text Color", Color.PINK); if (newColor != null) { settings.setProperty("highlightColor", "#" + Integer.toHexString(newColor.getRGB()).substring(2)); saveSettings(); } } public void changeColor(JTextArea textArea){ Color newColor = JColorChooser.showDialog(new JFrame(), "Choose Background Color", Color.LIGHT_GRAY); if (newColor != null) { settings.setProperty("bgColor", "#" + Integer.toHexString(newColor.getRGB()).substring(2)); textArea.setBackground(newColor); saveSettings(); } } private void saveSettings() { try (FileOutputStream fos = new FileOutputStream(settingsFile)) { settings.store(fos, null); } catch (IOException e) { e.printStackTrace(); } } private void loadSettings() { if (settingsFile.exists()) { try (FileInputStream fis = new FileInputStream(settingsFile)) { settings.load(fis); } catch (IOException e) { e.printStackTrace(); } } } private static int getTextWidth(String text, FontRenderContext frc) { if (text.isEmpty()) return 1; // Ensure minimum width for empty columns TextLayout layout = new TextLayout(text, new Font("Monospaced", Font.PLAIN, 12), frc); return (int) Math.ceil(layout.getBounds().getWidth() / 7.0); // Approximate width per char } public static void searchbox(Frame frame, JTextArea textArea, Color searchColor){ String word = JOptionPane.showInputDialog("Word Search"); if (word.isEmpty()) return; word = word.replace("-",""); Highlighter highlighter = textArea.getHighlighter(); highlighter.removeAllHighlights(); //if you search more than once it clears the previous highlights Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(searchColor); String text = textArea.getText(); int index = 0; try { while ((index = text.indexOf(word, index)) >= 0) { // Find all occurrences highlighter.addHighlight(index, index + word.length(), painter); index += word.length(); // Move past the last found word } if (Arrays.stream(highlighter.getHighlights()).findAny().isEmpty()) { JOptionPane.showMessageDialog(frame, "Did not find " + word, "ERROR", JOptionPane.ERROR_MESSAGE); } else { JOptionPane.showMessageDialog(null, new JScrollPane(textArea)); System.out.println(Arrays.stream(highlighter.getHighlights()).count()); } } catch (BadLocationException ex) { throw new RuntimeException(ex); } } private static void loadFileAndDisplay(JTextArea textArea) { JFileChooser fileChooser = new JFileChooser(); String filePath = System.getProperty("user.home"); FileNameExtensionFilter filter = new FileNameExtensionFilter("Text Files (*.txt)", "txt"); fileChooser.setFileFilter(filter); fileChooser.setCurrentDirectory(new File(filePath + "/Documents")); int returnValue = fileChooser.showOpenDialog(null); if (returnValue == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); System.out.println("Selected file: " + selectedFile.getAbsolutePath()); try { java.util.List rows = new ArrayList<>(); java.util.List columnWidths = new ArrayList<>(); int maxColumns = 0; FontRenderContext frc = new FontRenderContext(null, true, true); // Read file and store rows try (BufferedReader br = Files.newBufferedReader(selectedFile.toPath())) { String line; while ((line = br.readLine()) != null) { String[] columns = line.split("\\|", -1); // Preserve empty columns rows.add(columns); maxColumns = Math.max(maxColumns, columns.length); } } // Ensure all rows have the same number of columns for (String[] row : rows) { for (int i = 0; i < maxColumns; i++) { String value = (i < row.length) ? row[i] : ""; // Fill missing columns with empty string int displayWidth = getTextWidth(value, frc); if (columnWidths.size() <= i) { columnWidths.add(Math.max(1, displayWidth)); // Ensure at least 1 space width } else { columnWidths.set(i, Math.max(columnWidths.get(i), value.isEmpty() ? 1 : displayWidth)); } } } if (!rows.isEmpty()) { String formattedText = formatData(rows, columnWidths, frc); textArea.setText(formattedText); } else { textArea.setText("The selected file is empty."); } } catch (IOException x) { x.printStackTrace(); Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Error IOException", x); JOptionPane.showMessageDialog(null, "An error occurred reading the file. Please try again.", "Read File Error", JOptionPane.ERROR_MESSAGE); textArea.setText("Error reading file."); } } else { textArea.setText("No file was selected."); } } private static String formatData(java.util.List rows, List columnWidths, FontRenderContext frc) { StringBuilder output = new StringBuilder(); int columnCount = columnWidths.size(); // Generate column headers String[] headers = {"first","col2","0", "?","??"}; // Build header and separator lines StringBuilder headerLine = new StringBuilder(); StringBuilder separatorLine = new StringBuilder(); for (int i = 0; i < columnCount; i++) { int width = columnWidths.get(i); if(headers[i].length() > columnWidths.get(i)){ headerLine.append(String.format(" %-"+ headers[i].length() +"s ", headers[i])); separatorLine.append("-".repeat(headers[i].length()+2)); }else{ headerLine.append(String.format(" %-"+ width +"s ", headers[i])); separatorLine.append("-".repeat(width + 2)); } if (i < columnCount - 1) { headerLine.append("|"); separatorLine.append("+"); } } output.append(headerLine).append("\n"); output.append(separatorLine).append("\n"); // Append formatted rows for (String[] row : rows) { StringBuilder formattedLine = new StringBuilder(); for (int i = 0; i < columnCount; i++) { String value = (i < row.length) ? row[i] : " "; int adjustedWidth = Math.max(columnWidths.get(i), getTextWidth(headers[i], frc)); String adjustedValue = padToWidth(value, columnWidths.get(i)); if(headers[i].length() > columnWidths.get(i)){ //formattedLine.append(String.format(" %-" + headers[i].length() + "s ", value)); formattedLine.append(" ").append(adjustedValue).append(" "); } else { //formattedLine.append(String.format(" %-" + adjustedWidth + "s ", value)); formattedLine.append(" ").append(adjustedValue).append(" "); System.out.println(adjustedValue+":"+adjustedValue.length()); } if (i < columnCount - 1) { formattedLine.append("|"); } } output.append(formattedLine).append("\n"); } return output.toString(); } private static String padToWidth(String text, int width) { //int textWidth = getDisplayWidth(text); int textWidth = getDisplayWidth(text,new Font("Monospaced",Font.PLAIN,20)); int padding = width - textWidth; return text + " ".repeat(Math.max(0, padding)); } // private static int getDisplayWidth(String text) { // int width = 0; // for (char c : text.toCharArray()) { // Character.UnicodeBlock block = Character.UnicodeBlock.of(c); // // Set WIDE_CHAR_BLOCKS = new HashSet<>(); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.CJK_COMPATIBILITY_FORMS); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.CJK_RADICALS_SUPPLEMENT); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.KATAKANA); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.KATAKANA_PHONETIC_EXTENSIONS); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.HANGUL_SYLLABLES); // Korean Hangul // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.HANGUL_JAMO); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.HANGUL_COMPATIBILITY_JAMO); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.ENCLOSED_CJK_LETTERS_AND_MONTHS); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.BOPOMOFO); // WIDE_CHAR_BLOCKS.add(Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS); // // if (WIDE_CHAR_BLOCKS.contains(block)) { // width += 2; // These characters take up two spaces in monospaced fonts // } else if (Character.UnicodeBlock.of(c) == Character.UnicodeBlock.HIRAGANA){ // width += 2; // } else { // width += 1; // Regular Latin characters take up one space // } // // } // return width; // } // Dynamically measures the display width of a given string public static int getDisplayWidth(String text, Font font) { FontMetrics metrics = new JLabel().getFontMetrics(font); int width = 0; int spaceWidth = metrics.charWidth(' ');// Get width of a monospaced space FontRenderContext frc = new FontRenderContext(null, true, true); for (char c : text.toCharArray()) { TextLayout layout = new TextLayout(String.valueOf(c), font, frc); double charWidth = layout.getBounds().getWidth(); //int charWidth = metrics.charWidth(c); // Get pixel width of character // // Convert pixel width into monospaced "spaces" //width += Math.max(1, (int) Math.round(charWidth / (double) spaceWidth)); // //width += (int) Math.round(charWidth / (double) spaceWidth); //this is close if (charWidth > spaceWidth) { width += Math.max(1, (int) Math.round(charWidth / (double) spaceWidth)); } else { width += 1; // Treat half-width and Latin characters as taking 1 space } } return width ; } }