package cvbuilder.view; import cvbuilder.controller.CsvExportSettings; import cvbuilder.model.Address; import cvbuilder.model.User; import cvbuilder.model.UserGroup; import javax.swing.*; import java.awt.*; import java.io.*; import java.util.*; public class MainViewer { private final UserGroup userGroup; public MainViewer() { userGroup = UserGroup.getInstance(); // Create the main frame JFrame viewer = new JFrame("CV Builder"); viewer.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); viewer.setSize(800, 500); // Create menu bar JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("Menu"); menuBar.add(menu); viewer.setJMenuBar(menuBar); // Set up the Open and Save menu items setupMenu(viewer); // Add content to the frame populateMainPanel(viewer); // Make the frame visible after all content is added viewer.setVisible(true); } private void populateMainPanel(JFrame viewer) { JTabbedPane outer = new JTabbedPane(); JTabbedPane userT = new JTabbedPane(); JTabbedPane contactT = new JTabbedPane(); // User tab JPanel title = new JPanel(); title.setName("title"); JPanel name = new JPanel(); name.setName("name"); JPanel email = new JPanel(); email.setName("email"); populatePanel(title); populatePanel(name); populatePanel(email); userT.addTab("Title", title); userT.addTab("Name", name); userT.addTab("Email", email); outer.addTab("User", userT); // Contact tab JPanel phone = new JPanel(); phone.setName("phone"); JPanel address = new JPanel(); address.setName("address"); populatePanel(phone); populatePanel(address); contactT.addTab("Phone", phone); contactT.addTab("Address", address); outer.addTab("Contact", contactT); // Create main container with BorderLayout JPanel mainContainer = new JPanel(new BorderLayout()); mainContainer.add(outer, BorderLayout.CENTER); // Add footer with navigation buttons JPanel footer = createFooter(outer); mainContainer.add(footer, BorderLayout.SOUTH); viewer.getContentPane().add(mainContainer); viewer.revalidate(); } private void populatePanel(JPanel panel) { String panelName = panel.getName(); String titleName = panelName.substring(0, 1).toUpperCase() + panelName.substring(1); panel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), titleName )); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); ButtonGroup group = new ButtonGroup(); // Add "Include in CSV" checkbox at the top - but NOT for name or email panels if (!panelName.equalsIgnoreCase("name") && !panelName.equalsIgnoreCase("email")) { JPanel includePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); JCheckBox includeCheckbox = new JCheckBox("Include in CSV"); includeCheckbox.setSelected(true); // Default to selected includePanel.add(includeCheckbox); // Store selection in the CsvExportSettings includeCheckbox.addActionListener(e -> { boolean include = includeCheckbox.isSelected(); System.out.println("Include " + panelName + " in CSV: " + include); CsvExportSettings.getInstance().setInclude(panelName, include); }); panel.add(includePanel); panel.add(Box.createVerticalStrut(5)); panel.add(new JSeparator(JSeparator.HORIZONTAL)); panel.add(Box.createVerticalStrut(10)); } else { // For name and email, always set them to be included without showing checkbox CsvExportSettings.getInstance().setInclude(panelName, true); } if (userGroup.getUsers().isEmpty()) { panel.add(new JLabel("No users available")); return; } // Add existing users for (int i = 0; i < userGroup.getUsers().size(); i++) { User user = userGroup.getUsers().get(i); String value = user.getUserAttribute(panelName); // Format address display if this is the address panel String displayValue = value; if (panelName.equalsIgnoreCase("address")) { Address address = user.getAddress(); displayValue = address.getDisplayFormat(); } if (value != null && !value.isEmpty()) { // Create a panel for each entry with radio button and action buttons JPanel entryPanel = new JPanel(); entryPanel.setLayout(new BoxLayout(entryPanel, BoxLayout.X_AXIS)); // Create radio button with properly formatted display value JRadioButton radioButton = new JRadioButton(displayValue); group.add(radioButton); entryPanel.add(radioButton); // Add some spacing entryPanel.add(Box.createHorizontalStrut(10)); // Create Edit button JButton editButton = new JButton("Edit"); final int userIndex = i; editButton.addActionListener(e -> { String newValue; if (panelName.equalsIgnoreCase("address")) { newValue = showAddressDialog("Edit " + panelName, value); } else { newValue = showDialog("Edit " + panelName, value); } if (newValue != null && !newValue.isEmpty()) { // Update the user attribute switch (panelName.toLowerCase()) { case "title" -> user.setTitle(newValue); case "name" -> user.setName(newValue); case "email" -> user.setEmail(newValue); case "phone" -> user.setPhone(newValue); case "address" -> user.setAddress(newValue); } // Update the radio button label with properly formatted value if (panelName.equalsIgnoreCase("address")) { radioButton.setText(user.getAddress().getDisplayFormat()); } else { radioButton.setText(newValue); } } }); entryPanel.add(editButton); // Add some spacing entryPanel.add(Box.createHorizontalStrut(5)); // Create delete button JButton deleteButton = new JButton("Delete"); deleteButton.addActionListener(e -> { // Remove the user without confirmation userGroup.getUsers().remove(userIndex); // Refresh all panels JFrame frame = (JFrame) SwingUtilities.getWindowAncestor(panel); frame.getContentPane().removeAll(); populateMainPanel(frame); frame.revalidate(); frame.repaint(); }); entryPanel.add(deleteButton); // Add padding to align elements entryPanel.add(Box.createHorizontalGlue()); // Add the entry panel to the main panel panel.add(entryPanel); } } // Add a separator panel.add(Box.createVerticalStrut(20)); panel.add(new JSeparator(JSeparator.HORIZONTAL)); panel.add(Box.createVerticalStrut(10)); // Add the "Add Row" interface at the bottom JPanel addRowPanel = new JPanel(); addRowPanel.setLayout(new BoxLayout(addRowPanel, BoxLayout.X_AXIS)); addRowPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 80)); JTextField newValueField = new JTextField(15); JButton addButton = new JButton("Add Row"); addRowPanel.add(new JLabel("New " + panelName + ": ")); addRowPanel.add(newValueField); addRowPanel.add(Box.createHorizontalStrut(10)); addRowPanel.add(addButton); // Add row action addButton.addActionListener(e -> { String newValue = newValueField.getText().trim(); if (!newValue.isEmpty()) { // Create a new user User newUser = new User(); // Set all attributes to "Unknown" by default newUser.setName("Unknown"); newUser.setTitle("Unknown"); newUser.setEmail("Unknown"); newUser.setPhone("Unknown"); newUser.setAddress("Unknown"); // Set the specific attribute from the input field switch (panelName.toLowerCase()) { case "title" -> newUser.setTitle(newValue); case "name" -> newUser.setName(newValue); case "email" -> newUser.setEmail(newValue); case "phone" -> newUser.setPhone(newValue); case "address" -> newUser.setAddress(newValue); } // Add the new user to the group userGroup.addUser(newUser); // Refresh all panels JFrame frame = (JFrame) SwingUtilities.getWindowAncestor(panel); frame.getContentPane().removeAll(); populateMainPanel(frame); frame.revalidate(); frame.repaint(); // Clear the input field newValueField.setText(""); } else { JOptionPane.showMessageDialog(panel, "Please enter a value for the new " + panelName, "Empty Value", JOptionPane.WARNING_MESSAGE); } }); panel.add(addRowPanel); // Add vertical glue to push all components to the top panel.add(Box.createVerticalGlue()); } private String showDialog(String title, String initialValue) { JTextField textField = new JTextField(initialValue); JLabel label = new JLabel("Enter new text:"); JPanel panel = new JPanel(); panel.setLayout(new GridLayout(0, 1)); panel.add(label); panel.add(textField); JOptionPane pane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION); JDialog dialog = pane.createDialog("Rename Dialogue Box"); dialog.setVisible(true); // Process the user's choice Object value = pane.getValue(); if (value instanceof Integer) { int choice = (Integer) value; if (choice == JOptionPane.OK_OPTION) { String newValue = textField.getText().trim(); System.out.println("OK option pressed and set text to: " + newValue + " for tab: " + title); return newValue; } else if (choice == JOptionPane.CANCEL_OPTION) { System.out.println("Cancel option pressed for tab: " + title); } } dialog.dispose(); return null; } private String showAddressDialog(String title, String initialValue) { JTextField flatField = new JTextField(); JTextField addressLine1Field = new JTextField(); JTextField addressLine2Field = new JTextField(); JTextField postcodeField = new JTextField(); // Use the Address class to parse the address string Address address = new Address(initialValue); // Set field values from the Address object flatField.setText(address.getFlat()); addressLine1Field.setText(address.getAddressLine1()); addressLine2Field.setText(address.getAddressLine2()); postcodeField.setText(address.getPostcode()); JPanel panel = new JPanel(new GridLayout(0, 1)); panel.add(new JLabel("Flat (optional):")); panel.add(flatField); panel.add(new JLabel("Address line 1:")); panel.add(addressLine1Field); panel.add(new JLabel("Address line 2 (optional):")); panel.add(addressLine2Field); panel.add(new JLabel("Postcode:")); panel.add(postcodeField); int result = JOptionPane.showConfirmDialog(null, panel, title, JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (result == JOptionPane.OK_OPTION) { String newFlat = flatField.getText().trim(); String newAddressLine1 = addressLine1Field.getText().trim(); String newAddressLine2 = addressLine2Field.getText().trim(); String newPostcode = postcodeField.getText().trim(); // Create an Address object with the new values Address newAddress = new Address(""); newAddress.setFlat(newFlat); newAddress.setAddressLine1(newAddressLine1); newAddress.setAddressLine2(newAddressLine2); newAddress.setPostcode(newPostcode); // Return the CSV format of the address return newAddress.getCsvFormat(); } return null; } private JPanel createFooter(JTabbedPane tabbedPane) { JPanel footer = new JPanel(new BorderLayout()); footer.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); JButton prevButton = new JButton("Previous Section"); JButton nextButton = new JButton("Next Section"); // Style the buttons prevButton.setFocusPainted(false); nextButton.setFocusPainted(false); // Add action listeners prevButton.addActionListener(e -> { int currentIndex = tabbedPane.getSelectedIndex(); if (currentIndex > 0) { tabbedPane.setSelectedIndex(currentIndex - 1); } }); nextButton.addActionListener(e -> { int currentIndex = tabbedPane.getSelectedIndex(); if (currentIndex < tabbedPane.getTabCount() - 1) { tabbedPane.setSelectedIndex(currentIndex + 1); } }); // Add buttons to footer footer.add(prevButton, BorderLayout.WEST); footer.add(nextButton, BorderLayout.EAST); return footer; } private void setupMenu(JFrame viewer) { JMenuBar menuBar = viewer.getJMenuBar(); JMenu menu = menuBar.getMenu(0); // Get the existing "Menu" menu // Create Open menu item JMenuItem openItem = new JMenuItem("Open"); openItem.addActionListener(e -> openCsvFile(viewer)); // Create Save menu item JMenuItem saveItem = new JMenuItem("Save"); saveItem.addActionListener(e -> saveCsvFile(viewer)); menu.add(new JSeparator()); menu.add(openItem); menu.add(saveItem); } // Class variable to store the current CSV file path -- added default file data private String currentCsvFile = "data/cv_repo_0.csv"; private void openCsvFile(JFrame viewer) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setDialogTitle("Open CSV File"); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter() { public boolean accept(File f) { return f.isDirectory() || f.getName().toLowerCase().endsWith(".csv"); } public String getDescription() { return "CSV Files (*.csv)"; } }); int result = fileChooser.showOpenDialog(viewer); if (result == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); loadCsvFile(selectedFile.getAbsolutePath(), viewer); } } private void loadCsvFile(String filePath, JFrame viewer) { UserGroup userGroup = UserGroup.getInstance(); userGroup.clearUsers(); // Clear existing users try (BufferedReader r = new BufferedReader(new FileReader(filePath))) { // Skip the first line (Section,Sub-Section,Variants) r.readLine(); // Read all lines into memory so we can process by column String[] lines = r.lines().toArray(String[]::new); // First pass: determine maximum number of users int maxUserCount = 0; for (String line : lines) { String[] parts = line.split(","); int userCount = parts.length - 2; // Subtract first two columns maxUserCount = Math.max(maxUserCount, userCount); } // Create all users with default "Unknown" values for (int i = 0; i < maxUserCount; i++) { User user = new User(); user.setName("Unknown"); user.setTitle("Unknown"); user.setEmail("Unknown"); user.setPhone("Unknown"); user.setAddress("Unknown"); userGroup.addUser(user); } // Second pass: fill in actual values where available for (String line : lines) { String[] parts = line.split(","); if (parts.length < 3) continue; String sectionType = parts[0] + "," + parts[1]; // Process each user column (starting from column 2, index 2) for (int colIndex = 2; colIndex < parts.length; colIndex++) { int userIndex = colIndex - 2; // User index starts from 0 if (userIndex < userGroup.getUsers().size()) { User user = userGroup.getUsers().get(userIndex); String value = parts[colIndex].trim(); // Only update if the value is not empty if (!value.isEmpty()) { switch (sectionType) { case "User,Name" -> user.setName(value); case "User,Title" -> user.setTitle(value); case "User,Email" -> user.setEmail(value); case "Contact Information,Phone" -> user.setPhone(value); case "Contact Information,Address" -> user.setAddress(value); } } } } } // Store the current file path currentCsvFile = filePath; // Refresh the UI viewer.getContentPane().removeAll(); populateMainPanel(viewer); viewer.revalidate(); viewer.repaint(); System.out.println("Loaded " + maxUserCount + " users from " + filePath); } catch (IOException e) { JOptionPane.showMessageDialog(viewer, "Error loading CSV file: " + e.getMessage(), "File Error", JOptionPane.ERROR_MESSAGE); } } private void saveCsvFile(JFrame viewer) { if (currentCsvFile == null) { // No file loaded yet, show save dialog JFileChooser fileChooser = new JFileChooser(); fileChooser.setDialogTitle("Save CSV File"); fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter() { public boolean accept(File f) { return f.isDirectory() || f.getName().toLowerCase().endsWith(".csv"); } public String getDescription() { return "CSV Files (*.csv)"; } }); int result = fileChooser.showSaveDialog(viewer); if (result == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); String path = selectedFile.getAbsolutePath(); if (!path.toLowerCase().endsWith(".csv")) { path += ".csv"; } currentCsvFile = path; writeToCSV(currentCsvFile); } } else { // Use the existing file path writeToCSV(currentCsvFile); } } private void writeToCSV(String filePath) { // First read the existing file to preserve data for sections not included Map existingData = new HashMap<>(); try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { // Skip the header reader.readLine(); String line; while ((line = reader.readLine()) != null) { String[] parts = line.split(",", 3); // Split into section, subsection, and the rest if (parts.length >= 2) { String key = parts[0] + "," + parts[1]; // Store the whole line existingData.put(key, line.split(",")); } } } catch (IOException e) { // If file doesn't exist or can't be read, we'll create a new one // No need to handle this exception } try (PrintWriter writer = new PrintWriter(new FileWriter(filePath))) { // Get export settings CsvExportSettings exportSettings = CsvExportSettings.getInstance(); // Write header writer.println("Section,Sub-Section,Variants"); // Get all users UserGroup userGroup = UserGroup.getInstance(); // Build user data strings to append - we'll use these if the section should be included String[] nameData = new String[userGroup.getUsers().size()]; String[] titleData = new String[userGroup.getUsers().size()]; String[] emailData = new String[userGroup.getUsers().size()]; String[] phoneData = new String[userGroup.getUsers().size()]; String[] addressData = new String[userGroup.getUsers().size()]; for (int i = 0; i < userGroup.getUsers().size(); i++) { User user = userGroup.getUsers().get(i); nameData[i] = user.getName(); titleData[i] = user.getTitle(); emailData[i] = user.getEmail(); phoneData[i] = user.getPhone(); addressData[i] = user.getAddress().getCsvFormat(); } // Process and write each section based on inclusion settings // Name - always included writeSection(writer, existingData, "User,Name", nameData, true); // Title - conditionally included writeSection(writer, existingData, "User,Title", titleData, exportSettings.isIncluded("title")); // Email - always included writeSection(writer, existingData, "User,Email", emailData, true); // Phone - conditionally included writeSection(writer, existingData, "Contact Information,Phone", phoneData, exportSettings.isIncluded("phone")); // Address - conditionally included writeSection(writer, existingData, "Contact Information,Address", addressData, exportSettings.isIncluded("address")); } catch (IOException e) { JOptionPane.showMessageDialog(null, "Error saving CSV file: " + e.getMessage(), "File Error", JOptionPane.ERROR_MESSAGE); } } private void writeSection(PrintWriter writer, Map existingData, String section, String[] userData, boolean include) { if (include) { // This section should be updated with new data writer.print(section); for (String data : userData) { writer.print("," + data); } writer.println(); } else if (existingData.containsKey(section)) { // This section should be preserved as-is String[] parts = existingData.get(section); writer.print(parts[0] + "," + parts[1]); // Section and Sub-Section // Add the remaining data columns for (int i = 2; i < parts.length; i++) { writer.print("," + parts[i]); } writer.println(); } } }