package pickMaint; import static pickMaint.ExcelExporter.exportJTableToFile; import static pickMaint.ExcelExporter.openInExcel; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.sql.SQLException; import java.text.DateFormat; import java.text.NumberFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.table.TableCellRenderer; import com.toedter.calendar.JDateChooser; import net.miginfocom.swing.MigLayout; public class Editor extends JFrame { private static final long serialVersionUID = 1L; private final AS400Interface as400; private HashMap loadedShifts = new HashMap(); //Holds data for all loaded tabs. Key format: MM/dd/yyy-shift ex: 02/03/14-2 private HashMap loadedTables = new HashMap(); private HashMap loadedTableModels = new HashMap(); private HashMap loadedScrollPanes = new HashMap(); private final JTabbedPane tabbedPane; private final Editor self; private JMenuBar topMenu = new JMenuBar(); private JMenu fileMenu = new JMenu("File"); private JMenuItem newTab = new JMenuItem("New Tab"); private JMenu toolsMenu = new JMenu("Tools"); private JMenuItem newRecord = new JMenuItem("Add New Record"); private JMenuItem openInExcel = new JMenuItem("Export Current Tab to Excel"); private JMenuItem shiftSummary = new JMenuItem("Shift Summary"); /** * Dispatches a Runnable to the EDT that builds/displays a login prompt. * The credentials entered in this prompt are used to create an AS400Interface object. * This AS400Interface object is used to create/display a new Editor. * * @param args - Not used. * @throws SQLException - Shouldn't happen :) */ public static void main(String[] args) { java.awt.EventQueue.invokeLater(new Runnable() { @Override public void run() { JPanel loginPrompt = new JPanel(new MigLayout("fill")); final JComboBox users = new JComboBox(new String[]{ "WRAY", "AARON", "CALEB"}); JLabel userLabel = new JLabel("User:"); loginPrompt.add(userLabel, "grow"); loginPrompt.add(users, "grow, wrap"); final JPasswordField passwordField = new JPasswordField(10); passwordField.addAncestorListener(new RequestFocusListener()); JLabel passLabel = new JLabel("Password:"); loginPrompt.add(passLabel, "grow"); loginPrompt.add(passwordField, "grow, wrap"); final JComboBox dataSet = new JComboBox(new String[]{"PRODUCTION", "TEST"}); loginPrompt.add(dataSet, "grow, span 2 1, wrap"); int option = JOptionPane.showOptionDialog(null, loginPrompt, "Pick Maintenance Login - " + (new SimpleDateFormat("MM/dd/yyyy - HH:mm").format(Calendar.getInstance().getTime())), JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{ "Login!", "Quit" }, "Login!"); if(option == 0) java.awt.EventQueue.invokeLater(new Runnable() { @Override public void run() { AS400Interface connection = null; boolean productionData = dataSet.getSelectedIndex() == 0; try { connection = new AS400Interface((String)users.getSelectedItem(), new String(passwordField.getPassword()), productionData); new Editor(connection); } catch (SQLException e) { e.printStackTrace(); System.exit(ERROR); //Not much else we can do here. Are you connected to the network? } } }); } }); } public Editor(AS400Interface as400) throws SQLException { super("Pick Maintenance"); //Call super constructor, set title this.as400 = as400; //Save a reference to the AS400Interface self = this; //Store reference to this Editor object for accessing within anonymous inner classes setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Exit program gracefully when user closes JFrame setSize(1440, 810); //Set size of the JFrame this.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e) { self.as400.close(); super.windowClosing(e); } }); //Create action for "New Tab" menu option newTab.addActionListener(new ActionListener(){ private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { try { //Create prompt to get date and shift from user JPanel newTabPrompt = new JPanel(); JDateChooser dateChooser = new JDateChooser(new Date(), "MM/dd/yy"); JTextField shiftInput = new JTextField(3); shiftInput.addAncestorListener(new RequestFocusListener()); shiftInput.addFocusListener(new SelectAllOnFocusListener()); newTabPrompt.add(dateChooser); newTabPrompt.add(new JLabel("Shift")); newTabPrompt.add(shiftInput); //Display prompt int option = JOptionPane.showOptionDialog(self, newTabPrompt, "New Tab", JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{ "Add", "Cancel" }, "Add"); if(option == 0){ //If user clicked "Add" add a tab for the date and shift selected addTab(dateChooser.getDate(), Integer.parseInt(shiftInput.getText())); } } catch (SQLException e1) { JOptionPane.showMessageDialog(rootPane, e1.getMessage(), "Error Getting Data From AS400", JOptionPane.ERROR_MESSAGE); } catch(NumberFormatException e2){ LOGGER.log(Level.SEVERE, "Error parsing user input. Nothing loaded. See exception message for details.", e2); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rError parsing user input. Nothing loaded. See exception message for details.", "Whoops!", JOptionPane.ERROR_MESSAGE); } } }); newRecord.addActionListener(new ActionListener() { private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { String[] parse = tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()).split(" "); int shift = 0; try { shift = Integer.parseInt(parse[2].substring(0, 1)); } catch(Exception e1) { LOGGER.log(Level.SEVERE, "Failed to build key for date/shift. Better luck next time.", e1); return; } DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); Date date = null; try { date = formatter.parse(parse[0]); } catch (ParseException e1) { LOGGER.log(Level.SEVERE, "Failed to build key for date/shift. Better luck next time.", e1); return; } JPanel prompt = new JPanel(new MigLayout("fill")); JDateChooser dateDisp = new JDateChooser(date, "MM/dd/yy"); //Date chooser just for displaying date, not editable dateDisp.setEnabled(false); JTextField shiftDisp = new JTextField("" + shift); //Text field for displaying shift, not editable shiftDisp.setEnabled(false); final JTextField loomInp = new JTextField(4); loomInp.addAncestorListener(new RequestFocusListener()); //Yield focus to the loom input field when dialog displayed loomInp.addFocusListener(new SelectAllOnFocusListener()); //Select any text currently in the field on focus JTextField weaverInp = new JTextField(4); weaverInp.addFocusListener(new SelectAllOnFocusListener()); //Select any text currently in the field on focus JComboBox styleInp = new JComboBox(new String[]{" ", " BLN", " TRL", " SAM"}); final JTextField hoursInp = new JTextField(4); hoursInp.addFocusListener(new SelectAllOnFocusListener()); //Select any text currently in the field on focus final JTextField picksInp = new JTextField(4); picksInp.addFocusListener(new SelectAllOnFocusListener()); //Select any text currently in the field on focus JComboBox dtInp = new JComboBox(AS400Interface.getDtCodeChoices()); JTextField cutInp = new JTextField(4); cutInp.addFocusListener(new SelectAllOnFocusListener()); //Select any text currently in the field on focus JTextField cutSuffixInp = new JTextField(4); cutSuffixInp.addFocusListener(new SelectAllOnFocusListener()); //Select any text currently in the field on focus JComboBox typeInp = new JComboBox(new String[]{"B", "T"}); JCheckBox bonusInp = new JCheckBox(); hoursInp.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { update(); } @Override public void insertUpdate(DocumentEvent e) { update(); } @Override public void changedUpdate(DocumentEvent e) { update(); } private void update(){ try { float pph = AS400Interface.loomSpeedLookup(Integer.parseInt(loomInp.getText())); //Get max picks per hour for user entered loom double hours = Double.parseDouble(hoursInp.getText()); //get user entered hours picksInp.setText("" + (int) (pph * hours)); //Multiply, set pick input to product } catch(Exception e){ } //If we don't have all the required information: do nothing. } }); loomInp.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { update(); } @Override public void insertUpdate(DocumentEvent e) { update(); } @Override public void changedUpdate(DocumentEvent e) { update(); } private void update(){ try { float pph = AS400Interface.loomSpeedLookup(Integer.parseInt(loomInp.getText())); //Get max picks per hour for user entered loom double hours = Double.parseDouble(hoursInp.getText()); //get user entered hours picksInp.setText("" + (int) (pph * hours)); //Multiply, set pick input to product } catch(Exception e){ } //If we don't have all the required information: do nothing. } }); prompt.add(new JLabel("Date"), "align right, grow"); prompt.add(dateDisp, "align left, grow, wrap"); prompt.add(new JLabel("Shift"), "align right, grow"); prompt.add(shiftDisp, "align left, grow, wrap"); prompt.add(new JLabel("Loom"), "align right, grow"); prompt.add(loomInp, "align left, grow, wrap"); prompt.add(new JLabel("Weaver"), "align right, grow"); prompt.add(weaverInp, "align left, grow, wrap"); prompt.add(new JLabel("Style"), "align right, grow"); prompt.add(styleInp, "align left, grow, wrap"); prompt.add(new JLabel("Hours"), "align right, grow"); prompt.add(hoursInp, "align left, grow, wrap"); prompt.add(new JLabel("Picks"), "align right, grow"); prompt.add(picksInp, "align left, grow, wrap"); prompt.add(new JLabel("Down Time Code"), "align right, grow"); prompt.add(dtInp, "align left, grow, wrap"); prompt.add(new JLabel("Cut Number"), "align right, grow"); prompt.add(cutInp, "align left, grow, wrap"); prompt.add(new JLabel("Cut Suffix"), "align right, grow"); prompt.add(cutSuffixInp, "align left, grow, wrap"); prompt.add(new JLabel("Type Code"), "align right, grow"); prompt.add(typeInp, "align left, grow, wrap"); prompt.add(new JLabel("No Bonus"), "align right, grow"); prompt.add(bonusInp, "align left, grow, wrap"); int option = JOptionPane.showOptionDialog(self, prompt, "New Record", JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{ "Add", "Cancel" }, "Add"); if(option == 0){ int loom = 0; int weaver = 0; String style1 = ""; int picks = 0; double hours = 0; String dtCode = ""; int cut1 = 0; String cut2 = ""; String typeCode = ""; boolean noBonus = false; try { loom = Integer.parseInt(loomInp.getText()); if(loom < 201 || loom > 317) throw new NumberFormatException("Loom number out of range: " + loom); weaver = Integer.parseInt(weaverInp.getText()); if(AS400Interface.weaverLookup(weaver) == null) throw new NumberFormatException("Weaver doesn't exist: " + weaver); style1 = (String) styleInp.getSelectedItem(); picks = Integer.parseInt(picksInp.getText()); if(picks < 0 || picks > 250) throw new NumberFormatException("Picks value not sane: " + picks); hours = Double.parseDouble(hoursInp.getText()); if(hours < 0 || hours > 12) throw new NumberFormatException("Hours Value not sane: " + hours); dtCode = ((String) dtInp.getSelectedItem()).split(" ")[0]; if(dtCode.equalsIgnoreCase("NONE")) dtCode = " "; cut1 = Integer.parseInt(cutInp.getText()); if(cut1 < 0 || cut1 > 999) throw new NumberFormatException("Cut number is out of range: " + cut1); if(cutSuffixInp.getText().length() > 2) cut2 = cutSuffixInp.getText().substring(0, 2); else cut2 = cutSuffixInp.getText(); typeCode = ((String) typeInp.getSelectedItem()); noBonus = bonusInp.isSelected(); } catch(NumberFormatException e2){ LOGGER.log(Level.SEVERE, "Error parsing user input. Nothing added. See exception message for details.", e2); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rError parsing user input. Nothing added. See exception message for details.", "Whoops!", JOptionPane.ERROR_MESSAGE); return; } try { self.as400.addRecord(date, loom, shift, weaver, style1, hours, picks, dtCode, cut1, cut2, typeCode, noBonus); reloadTab(makeKey(date, shift)); } catch(SQLException e2) { LOGGER.log(Level.SEVERE, "Error writing record to database. Better luck next time", e2); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rA databse transaction error occured. Nothing added. See exception message for details.", "Whoops!", JOptionPane.ERROR_MESSAGE); } catch (ParseException e2) { LOGGER.log(Level.SEVERE, "Error reloading tab. Add it manually. See exception message for details.", e2); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rError reloading tab. Add it manually. See exception message for details.", "Whoops!", JOptionPane.ERROR_MESSAGE); } } } }); openInExcel.addActionListener(new ActionListener() { private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { String[] parse = tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()).split(" "); int shift = 0; try { shift = Integer.parseInt(parse[2].substring(0, 1)); } catch(Exception e1) { LOGGER.log(Level.SEVERE, "Failed to build key for date/shift. Better luck next time.", e1); return; } DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); Date date = null; try { date = formatter.parse(parse[0]); } catch (ParseException e1) { LOGGER.log(Level.SEVERE, "Failed to build key for date/shift. Better luck next time.", e1); return; } try { exportJTableToFile(loadedTables.get(makeKey(date, shift)), "out.xls"); openInExcel("out.xls"); } catch (IOException e1) { LOGGER.log(Level.SEVERE, "Some kind of file I/O took a dump while exporting table. Sorry.", e1); return; } } }); shiftSummary.addActionListener(new ActionListener(){ private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { NumberFormat percentFormat = NumberFormat.getPercentInstance(); percentFormat.setMaximumFractionDigits(3); String[] parse = tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()).split(" "); int shift = 0; try { shift = Integer.parseInt(parse[2].substring(0, 1)); } catch(Exception e1) { LOGGER.log(Level.SEVERE, "Failed to build key for date/shift. Better luck next time.", e1); return; } DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); Date date = null; try { date = formatter.parse(parse[0]); } catch (ParseException e1) { LOGGER.log(Level.SEVERE, "Failed to build key for date/shift. Better luck next time.", e1); return; } float shiftLength = -1; float prodLoomHrs = 0; int prodLoomPicks = 0; float noBonHours = 0; int noBonPicks = 0; float sampHours = 0; int sampPicks = 0; float shiftEff = 0; try { prodLoomHrs = self.as400.getProductionLoomHours(date, shift); prodLoomPicks = self.as400.getProductionPicks(date, shift); noBonHours = self.as400.getNoBonusLoomHours(date, shift); noBonPicks = self.as400.getNoBonusPicks(date, shift); sampHours = self.as400.getSampleLoomHours(date, shift); sampPicks = self.as400.getSamplePicks(date, shift); shiftLength = self.as400.getShiftLength(date, shift); shiftEff = self.as400.getShiftEfficiency(date, shift); } catch (SQLException e1) { LOGGER.log(Level.SEVERE, "Something broke.", e1); } float prodLooms = prodLoomHrs / shiftLength; float sampPicksPct = (float) sampPicks / prodLoomPicks; float sampHrsPct = sampHours / prodLoomHrs; JPanel dialog = new JPanel(new MigLayout("fill")); dialog.add(new JLabel("Shift Efficiency:"), "grow, align right"); dialog.add(new JLabel(percentFormat.format(shiftEff)), "grow, wrap"); dialog.add(new JLabel("Shift Length:"), "grow, align right"); dialog.add(new JLabel(shiftLength + " hours"), "grow, wrap"); dialog.add(new JLabel("Production Loom Hours:"), "grow, align right"); dialog.add(new JLabel(prodLoomHrs + " hours"), "grow, wrap"); dialog.add(new JLabel("Number of Looms:"), "grow, align right"); dialog.add(new JLabel("" + prodLooms), "grow, wrap"); dialog.add(new JLabel("Production Picks:"), "grow, align right"); dialog.add(new JLabel("" + prodLoomPicks), "grow, wrap"); dialog.add(new JLabel("Sample Hours:"), "grow, align right"); dialog.add(new JLabel(sampHours + " hours (" + percentFormat.format(sampHrsPct) + ")"), "grow, wrap"); dialog.add(new JLabel("Sample Picks:"), "grow, align right"); dialog.add(new JLabel(sampPicks + " (" + percentFormat.format(sampPicksPct) + ")"), "grow, wrap"); dialog.add(new JLabel("No Bonus Looms:"), "grow, align right"); dialog.add(new JLabel((noBonHours / 8.0) + ""), "grow, wrap"); dialog.add(new JLabel("No Bonus Picks:"), "grow, align right"); dialog.add(new JLabel(noBonPicks + ""), "grow, wrap"); JOptionPane.showOptionDialog(self, dialog, "Shift Summary", JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{ "Ok" }, "Ok"); } }); JPanel contentPane = new JPanel(new BorderLayout()); //Main Panel - Everything gets added to this newTab.setAccelerator(KeyStroke.getKeyStroke('t')); newRecord.setAccelerator(KeyStroke.getKeyStroke('n')); shiftSummary.setAccelerator(KeyStroke.getKeyStroke('s')); //Set up menu bar fileMenu.add(newTab); toolsMenu.add(newRecord); toolsMenu.add(openInExcel); toolsMenu.addSeparator(); toolsMenu.add(shiftSummary); topMenu.add(fileMenu); topMenu.add(toolsMenu); contentPane.add(topMenu, BorderLayout.NORTH); //Add menu bar to the top of the UI //Set up tabbed pane tabbedPane = new JTabbedPane(); //Add tabs for last three shifts that ran loadMostRecentDataByShift(1); loadMostRecentDataByShift(2); loadMostRecentDataByShift(3); contentPane.add(tabbedPane, BorderLayout.CENTER); //Add tabbed pane to the center of the UI setContentPane(contentPane); //Set the content pane of the JFrame to our JPanel we've created setLocationRelativeTo(null); setVisible(true); //Display the UI } private void loadMostRecentDataByShift(int shift) { Calendar cal = Calendar.getInstance(); boolean done = false; int dayOfWeek = -1; while (!done) { try { addTab(cal.getTime(), shift); //an SQL exception is thrown for an empty result set dropping us into the catch block below done = true; //No exception thrown, tab must've loaded okay set done = true; to exit loop } catch (SQLException e) { /* * In here we basically want to set the calendar to the previous day so we can try to load that. There some extra logic to * avoid Saturday and Sunday dates to help minimize pointless database transactions. */ do { cal.add(Calendar.DAY_OF_MONTH, -1); //Subtract a day from the current date dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); //Store day of the week } while (dayOfWeek == Calendar.SATURDAY //if Saturday or Sunday: repeat. || dayOfWeek == Calendar.SUNDAY); } } } /** * Create new tab for the specified date and shift * @param date - integer date in yyMMdd format. Ex: 140203 = Feb. 3, 2014 * @param shift - integer shift should be in the set [1, 2, 3] * @throws SQLException */ @SuppressWarnings("unused") private void addTab(int date, int shift) throws SQLException{ loadShiftDetail(date, shift); //Query server for data String key = makeKey(date, shift); //Form key to retrieve objects from HashMaps JScrollPane c = loadedScrollPanes.get(key); //Grab the ScrollPane from HashMap tabbedPane.add(formatKeyForDisplay(key), c); //Add ScrollPane to TabbedPane tabbedPane.setSelectedComponent(c); //Set focus to newly added ScrollPane tabbedPane.setTabComponentAt(tabbedPane.indexOfComponent(c), new ButtonTabComponent(tabbedPane, this)); //Add close button to tab } /** * Overloaded version to take a Java.util.Date for first arg instead of an integer. * @param date - Java.util.Date object representing the desired date * @param shift - integer shift should be in the set [1, 2, 3] * @throws SQLException */ private void addTab(Date date, int shift) throws SQLException{ loadShiftDetail(date, shift); //Query server for data String key = makeKey(date, shift); //Form key to retrieve objects from HashMaps JScrollPane c = loadedScrollPanes.get(key); //Grab the ScrollPane from HashMap tabbedPane.add(formatKeyForDisplay(key), c); //Add ScrollPane to TabbedPane tabbedPane.setSelectedComponent(c); //Set focus to newly added ScrollPane tabbedPane.setTabComponentAt(tabbedPane.indexOfComponent(c), new ButtonTabComponent(tabbedPane, this)); //Add close button to tab } /** * Overloaded to accept a single integer date. Creates a Java.util.Date object via * a static utility method and calls loadShiftDetail(Date, intDate, shift) * @param date - integer date in yyMMdd format. Ex: 140203 = Feb. 3, 2014 * @param shift - integer shift should be in the set [1, 2, 3] * @throws SQLException */ private void loadShiftDetail(int date, int shift) throws SQLException{ loadShiftDetail(AS400Interface.intToDate(date), date, shift); } /** * Overloaded to accept a single java.util.Date. Creates an integer representation of the * date in yyMMdd format and calls loadShiftDetail(Date, intDate, shift) * @param date - Java.util.Date object representing the desired date * @param shift - integer shift should be in the set [1, 2, 3] * @throws SQLException */ private void loadShiftDetail(Date date, int shift) throws SQLException{ loadShiftDetail(date, AS400Interface.dateToInt(date), shift); } /** * Never called directly; use one of the overloaded helper methods instead. * This method first creates the String key used by all hashmaps for this date/shift combo. * The shift detail is then fetched from the server in the form of a LoomDSRecord[]. * A reference to this array is stored in the loadedShifts HashMap * A LoomDSTableModel is created and loaded with the LoomDSRecord[] * A JTable is created using this TableModel * A Mouse listener is added to the JTable to facilitate right clicking of table rows * A ScrollPane is created using this JTable. This gets added to the new tab * A reference to the TableModel, JTable, and ScrollPane is stored in the respective HashMaps. * * Key format for the hash maps is "MM/dd/yyyy-SHIFT" so 3rd shift on Feb. 3, 2014 would be "02/03/2014-3" * @param date - A java.util.Date object for the desired date * @param intDate - an integer representation of date in yyMMdd format * @param shift - integer shift should be in the set [1, 2, 3] * @throws SQLException */ private void loadShiftDetail(Date date, int intDate, int shift) throws SQLException{ //Create Key final String key = new SimpleDateFormat("MM/dd/yyyy-").format(date) + shift; //Check if tab is already loaded if(loadedShifts.get(key) != null){ JOptionPane.showMessageDialog(self, "Tab already loaded!", "Whoops!", JOptionPane.ERROR_MESSAGE); return; } //Get data from server, store in HashMap loadedShifts.put(key, as400.getShiftDetail(intDate, shift)); LoomDSTableModel tableModel = new LoomDSTableModel(); //Create new TableModel tableModel.loadTable(loadedShifts.get(key)); //Load with data from server final JTable table = new JTable(tableModel){ private static final long serialVersionUID = 7479372430475297633L; private final Color HILIGHTED_ROW = new Color(184,207,229); //Got this with a color picker, hopefully it doesn't change on me private int lastRowDrawn = 0; public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { Component c = super.prepareRenderer(renderer, row, column); if(c.getBackground().equals(HILIGHTED_ROW)) //Check if current bg color is the blue for a highlighted row return c; //Do nothing if it is LoomDSTableModel model = (LoomDSTableModel) getModel(); if(model.highlightSample(row)){ lastRowDrawn = row; c.setBackground(new Color(255,248,220)); } else if(model.highlightZero(row)) { lastRowDrawn = row; c.setBackground(new Color(255,202,202)); } else if(!model.bonus(row) && row != lastRowDrawn){ lastRowDrawn = row; if(row % 2 == 0){ c.setBackground(Color.LIGHT_GRAY); } else { c.setBackground(new Color(205,201,201)); } } else if(row != lastRowDrawn){ lastRowDrawn = row; c.setBackground(new Color(255,255,255)); } return c; } }; table.addMouseListener(new MouseAdapter() { /* * This class enables row selection with the right mouse button. It also displays our * JPopupMenu on right click. */ private String hashKey = key; @Override public void mouseReleased(MouseEvent e) { int r = table.rowAtPoint(e.getPoint()); if (r >= 0 && r < table.getRowCount()) { table.setRowSelectionInterval(r, r); } else { table.clearSelection(); } int rowindex = table.getSelectedRow(); if (rowindex < 0) return; if (e.isPopupTrigger() && e.getComponent() instanceof JTable ) { JPopupMenu popup = createRightClickMenu(table, r, hashKey); popup.show(e.getComponent(), e.getX(), e.getY()); } } }); JScrollPane scrollPane = new JScrollPane(table); //Create ScrollPane for JTable loadedTables.put(key, table); //Store JTable in HashMap loadedTableModels.put(key, tableModel); //Store TableModel in HashMap loadedScrollPanes.put(key, scrollPane); //Store ScrollPane in HashMap } /** * Overloaded to accept a single integer date. * * First removes the tab associated with the key formed by the date and shift provided. * Then, Removes all objects from all HashMaps associated with the key formed by the date and shift provided. * * @param date - integer date in yyMMdd format. Ex: 140203 = Feb. 3, 2014 * @param shift - integer shift should be in the set [1, 2, 3] * @throws SQLException */ public void unloadShiftDetail(int date, int shift){ unloadShiftDetail(makeKey(date, shift)); } /** * Overloaded version to take a java.util.Date and an integer shift * * @param date - A java.util.Date object for the desired date * @param shift - integer shift should be in the set [1, 2, 3] */ public void unloadShiftDetail(Date date, int shift){ unloadShiftDetail(makeKey(date, shift)); } /** * First removes the tab associated with the key formed by the date and shift provided. * Then, Removes all objects from all HashMaps associated with the key formed by the date and shift provided. * * @param key - The HashMap key for the objects to unload */ public void unloadShiftDetail(String key){ tabbedPane.remove(loadedScrollPanes.get(key)); //Destroy Tab, if it exists loadedShifts.remove(key); //Remove LoomDSRecord[] from HashMap loadedTables.remove(key); //Remove JTable from HashMap loadedTableModels.remove(key); //Remove TableModel from HashMap loadedScrollPanes.remove(key); //Remove ScrollPane from HashMap } /** * Overloaded to take an integer date in yyMMdd format. Returns the array of LoomDSRecord objects * associated with the provided date/shift combination. * @param date - integer date in yyMMdd format. Ex: 140203 = Feb. 3, 2014 * @param shift - integer shift should be in the set [1, 2, 3] * @return An array of LoomDSRecord objects */ @SuppressWarnings("unused") private LoomDSRecord[] getLoadedDetail(int date, int shift){ return loadedShifts.get(makeKey(date, shift)); } /** * Returns the array of LoomDSRecord objects associated with the provided date/shift combination. * @param date - A java.util.Date object for the desired date * @param shift - integer shift should be in the set [1, 2, 3] * @return An array of LoomDSRecord objects */ @SuppressWarnings("unused") private LoomDSRecord[] getLoadedDetail(Date date, int shift){ return loadedShifts.get(makeKey(date, shift)); } /** * Overloaded to take an integer date in yyMMdd format. * Creates the string key needed to retrieve an objects from the hash maps out of the supplied * date and shift. * @param date - integer date in yyMMdd format. Ex: 140203 = Feb. 3, 2014 * @param shift - integer shift should be in the set [1, 2, 3] * @return String HashMap key */ private String makeKey(int date, int shift){ return makeKey(AS400Interface.intToDate(date), shift); } /** * Creates the string key needed to retrieve an objects from the hash maps out of the supplied * date and shift. * @param date - A java.util.Date object for the desired date * @param shift - integer shift should be in the set [1, 2, 3] * @return String HashMap key */ private String makeKey(Date date, int shift){ return new SimpleDateFormat("MM/dd/yyyy-").format(date) + shift; } /** * Totally destroys tab associated with the supplied key and then reloads it. * * @param key - The HashMap key for the tab to be destroyed/loaded * @throws ParseException * @throws SQLException */ private void reloadTab(String key) throws ParseException, SQLException{ String[] parse = key.split("-"); int shift = Integer.parseInt(parse[1]); DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); Date date = formatter.parse(parse[0]); unloadShiftDetail(key); addTab(date, shift); } /** * Reloads the data into the table model for the given key. * This is used to refresh the data in the table model to reflect changes made to any * LoomDSRecord objects associated with it. * @param key */ private void refreshTable(String key){ loadedTableModels.get(key).loadTable(loadedShifts.get(key)); } /** * Takes the supplied HashMap key and formats it to be used as the title for a tab. * @param key - String HashMap key * @return Formatted key string */ private String formatKeyForDisplay(String key){ String[] derp = key.split("-"); int shift = Integer.parseInt(derp[1]); return derp[0] + " - " + ((shift == 1) ? "1st" : (shift == 2) ? "2nd" : (shift == 3) ? "3rd" : "" + shift) + " Shift"; } private static double round(double value, int places) { if (places < 0) throw new IllegalArgumentException(); BigDecimal bd = new BigDecimal(value); bd = bd.setScale(places, RoundingMode.HALF_UP); return bd.doubleValue(); } /** * This method creates a JPopupMenu to display when a row of a table is right clicked. * * This is a long one... 6 anonymous inner classes for each of the menu options. * * @param table - The table object that was right-clicked * @param row - the row of the table that was right clicked * @param key - the key needed for getting objects from the HashMaps for this table. * @return the JPopupMenu */ private JPopupMenu createRightClickMenu(JTable table, int row, final String key){ final LoomDSRecord record = loadedShifts.get(key)[row]; JPopupMenu ret = new JPopupMenu("Loom " + table.getValueAt(row, 0)); JMenuItem adjPicks = new JMenuItem("Adjust Picks (Loom " + table.getValueAt(row, 0) + ")"); JMenuItem adjHours = new JMenuItem("Adjust Hours (Loom " + table.getValueAt(row, 0) + ")"); JMenuItem addSamp = new JMenuItem("Add Sample Cut (Loom " + table.getValueAt(row, 0) + ")"); JMenuItem chgWeav = new JMenuItem("Change Weaver (Loom " + table.getValueAt(row, 0) + ")"); JMenuItem setDT = new JMenuItem("Set Down Time Code (Loom " + table.getValueAt(row, 0) + ")"); JMenuItem delete = new JMenuItem("Delete Record (Loom " + table.getValueAt(row, 0) + ")"); adjPicks.addActionListener(new ActionListener(){ private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { int max = record.getMaxPicks(); double hours = record.getHours(); JLabel msg = new JLabel("The maximum picks for " + hours + " hours is " + max); JLabel label = new JLabel("Picks:"); JTextField input = new JTextField("" + record.getPicks(), 4); input.addAncestorListener(new RequestFocusListener()); input.addFocusListener(new SelectAllOnFocusListener()); JPanel pane = new JPanel(new MigLayout("fill")); pane.add(msg, "span 2, align center, wrap"); pane.add(label, "align right"); pane.add(input, "align left, wrap"); int option = JOptionPane.showOptionDialog(self, pane, "Adjust Picks - Loom " + record.getLoomNumber(), JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{ "Change", "Cancel" }, "Change"); if(option == 0){ int backup = record.getPicks(); try { int picks = Integer.parseInt(input.getText()); if(picks < 0 || picks > 250) throw new NumberFormatException("Pick value is not sane."); //Insane amount of picks, exit if(backup == picks) return; //Might as well not hit the server with a transaction if we're not actually changing anything record.setPicks(picks); //Change the picks in the LoomDSRecord object } catch(NumberFormatException e2){ JOptionPane.showMessageDialog(self, "Non-negative integer values <= 250 please!\n\rNo changes made.", "Bogus Input", JOptionPane.ERROR_MESSAGE); LOGGER.log(Level.SEVERE, "User passed us bogus input. Ignoring..", e2); return; } try { as400.adjustPicks(record); //This actually commits the changes to the server refreshTable(key); //If the changes were committed successfully, reload the table } catch (SQLException e1) { LOGGER.log(Level.SEVERE, "Database transaction failed when attempting to update picks. Reverting Changes.", e1); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rSomething went wrong when applying this change to the AS400", "Update Failed", JOptionPane.ERROR_MESSAGE); record.setPicks(backup); //Revert picks in LoomDSRecord object to previous value since DB transaction failed } } } }); chgWeav.addActionListener(new ActionListener(){ private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { int backup = record.getWeaverNumber(); JLabel msg = new JLabel("Current Weaver is: " + record.getWeaverNameDisplay()); JLabel label = new JLabel("New Weaver #"); JTextField input = new JTextField("" + record.getWeaverNumber(), 4); input.addAncestorListener(new RequestFocusListener()); input.addFocusListener(new SelectAllOnFocusListener()); JPanel prompt = new JPanel(new MigLayout("fill")); prompt.add(msg, "span 2, wrap"); prompt.add(label, "align right"); prompt.add(input, "align left, wrap"); int option = JOptionPane.showOptionDialog(self, prompt, "Change Weaver - Loom " + record.getLoomNumber(), JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{ "Change", "Cancel" }, "Change"); if(option == 0){ try { int newWeav = Integer.parseInt(input.getText()); if(AS400Interface.weaverLookup(newWeav) == null) throw new NumberFormatException("Weaver number doesn't exist. Did you add it after you launched this application?"); record.setWeaverNumber(newWeav); } catch(NumberFormatException e2){ LOGGER.log(Level.WARNING, "User passed us bogus input. Ignoring.", e2); JOptionPane.showMessageDialog(self, "Weaver Number Doesn't Exist!\n\rDid you add it after you launched this application?", "Bogus Input", JOptionPane.ERROR_MESSAGE); return; } try { as400.changeWeaver(record, backup); //This actually commits the changes to the server refreshTable(key); //If the changes were committed successfully, reload the table } catch (SQLException e1) { LOGGER.log(Level.SEVERE, "Database transaction failed when attempting to update weaver. Reverting Changes.", e1); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rSomething went wrong when applying this change to the AS400", "Update Failed", JOptionPane.ERROR_MESSAGE); record.setWeaverNumber(backup); //Revert weaver in LoomDSRecord object to previous value since DB transaction failed } } } }); setDT.addActionListener(new ActionListener(){ private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { boolean updateHours = false; String backup = record.getDownTimeCode(); double hrsBackup = record.getHours(); final int pksBackup = record.getPicks(); JComboBox choice = new JComboBox(AS400Interface.getDtCodeChoices()); JLabel msg = new JLabel(); final JTextField hrsAdjustment = new JTextField(4); String dialogTitle = ""; String[] dialogOptions = new String[2]; dialogOptions[1] = "Cancel"; if(backup.length() == 0) { msg.setText("Add a down time reason code to loom " + record.getLoomNumber()); dialogTitle = "Set Down Time Reason Code - Loom "; dialogOptions[0] = "Set"; } else { msg.setText("Down time reason code is currently: " + record.getformattedDowntimeReason()); dialogTitle = "Change Down Time Reason Code - Loom "; dialogOptions[0] = "Change"; } JPanel prompt = new JPanel(new MigLayout("fill")); prompt.add(msg, "span 2, align center, wrap"); prompt.add(choice, "span 2, align center, wrap"); prompt.add(new JLabel("Hours"), "align right, grow"); prompt.add(hrsAdjustment, "align left"); int option = JOptionPane.showOptionDialog(self, prompt, dialogTitle + record.getLoomNumber(), JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, dialogOptions, dialogOptions[0]); if(option == 0){ if(((String) choice.getSelectedItem()).equalsIgnoreCase("NONE") && backup.length() == 0) return; String newDtCode = ((String) choice.getSelectedItem()).split(" ")[0]; if(backup.equalsIgnoreCase(newDtCode)) return; if(newDtCode.equalsIgnoreCase("NONE")) newDtCode = ""; record.setDownTimeCode(newDtCode); try { double adj = Double.parseDouble(hrsAdjustment.getText()); // Attempt to parse duration of downtime from input box record.setHours(hrsBackup - adj); // Subtract length of downtime from loom's total hours record.setPicks(Math.min(pksBackup, record.getMaxPicks())); // Adjust total picks to the lesser of: new maximum for the new amount of looms hours - or - what was already entered updateHours = true; } catch (NumberFormatException e3) { updateHours = false; } try { as400.changeDtCode(record); //This actually commits the changes to the server if(updateHours) { as400.adjustHours(record); as400.adjustPicks(record); } refreshTable(key); //If the changes were committed successfully, reload the table } catch (SQLException e1) { LOGGER.log(Level.SEVERE, "Database transaction failed when attempting to update DT code. Reverting Changes.", e1); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rSomething went wrong when applying this change to the AS400", "Update Failed", JOptionPane.ERROR_MESSAGE); record.setDownTimeCode(backup); //Revert dt code in LoomDSRecord object to previous value since DB transaction failed record.setHours(hrsBackup); record.setPicks(pksBackup); } } } }); adjHours.addActionListener(new ActionListener() { private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { double backup = record.getHours(); JLabel label = new JLabel("Hours"); JTextField input = new JTextField("" + record.getHours(), 5); input.addAncestorListener(new RequestFocusListener()); input.addFocusListener(new SelectAllOnFocusListener()); JPanel prompt = new JPanel(new MigLayout("fill")); prompt.add(label, "align right, grow"); prompt.add(input, "align left, grow, wrap"); int option = JOptionPane.showOptionDialog(self, prompt, "Adjust Hours - Loom " + record.getLoomNumber(), JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{ "Change", "Cancel" }, "Change"); if(option == 0){ try{ double hours = Double.parseDouble(input.getText()); if(hours < 0 || hours > 12.0) throw new NumberFormatException("Hours value not sane."); if(hours == backup) return; record.setHours(hours); record.setPicks(Math.min(record.getPicks(), record.getMaxPicks())); } catch(NumberFormatException e2){ JOptionPane.showMessageDialog(self, "Non-negative decimal values <= 12.0 please!\n\rNo changes made.", "Bogus Input", JOptionPane.ERROR_MESSAGE); LOGGER.log(Level.SEVERE, "User passed us bogus input. Ignoring..", e2); return; } try { as400.adjustHours(record); //This actually commits the changes to the server as400.adjustPicks(record); refreshTable(key); //If the changes were committed successfully, reload the table } catch (SQLException e1) { LOGGER.log(Level.SEVERE, "Database transaction failed when attempting to update hours. Reverting Changes.", e1); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rSomething went wrong when applying this change to the AS400", "Update Failed", JOptionPane.ERROR_MESSAGE); record.setHours(backup); //Revert hours in LoomDSRecord object to previous value since DB transaction failed } } } }); addSamp.addActionListener(new ActionListener() { private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { final double hrsBackup = record.getHours(); final int pksBackup = record.getPicks(); final int distanceFromMax = record.getMaxPicks() - record.getPicks(); final JTextField pksInput = new JTextField(4); pksInput.addFocusListener(new SelectAllOnFocusListener()); final JTextField hrsInput = new JTextField("" + hrsBackup, 5); int derp = ((int) (record.getPicksPerHourNoBonus() * Double.parseDouble(hrsInput.getText()))); final JLabel msg = new JLabel("Enter hours and picks for sample cut.
Values cannot exceed " + hrsBackup + " hours, " + pksBackup + " picks

Range that will not affect total picks: " + Math.max((derp - distanceFromMax), 0) + " - " + derp); pksInput.setText("" + ((int) (record.getPicksPerHourNoBonus() * Double.parseDouble(hrsInput.getText())))); hrsInput.addAncestorListener(new RequestFocusListener()); hrsInput.addFocusListener(new SelectAllOnFocusListener()); hrsInput.getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { update(); } public void removeUpdate(DocumentEvent e) { update(); } public void insertUpdate(DocumentEvent e) { update(); } private void update(){ try { int maxSampPicks; if(hrsInput.getText().startsWith("m")) { double minutes = (double) Integer.parseInt(hrsInput.getText().substring(1)); maxSampPicks = (int) (record.getPicksPerHourNoBonus() * round(minutes / 60.0, 2)); } else { maxSampPicks = (int) (record.getPicksPerHourNoBonus() * Double.parseDouble(hrsInput.getText())); } pksInput.setText("" + maxSampPicks); msg.setText("Enter hours and picks for sample cut.
Values cannot exceed " + hrsBackup + " hours, " + pksBackup + " picks

Range that will not affect total picks: " + Math.max((maxSampPicks - distanceFromMax), 0) + " - " + maxSampPicks); } catch(Exception e){ } } }); JTextField cut1Input = new JTextField(3); cut1Input.addFocusListener(new SelectAllOnFocusListener()); JTextField cut2Input = new JTextField(3); cut2Input.addFocusListener(new SelectAllOnFocusListener()); JPanel prompt = new JPanel(new MigLayout("fill")); prompt.add(msg, "span 4, align center, wrap"); prompt.add(new JLabel("Hours"), "align right, grow"); prompt.add(hrsInput, "align left, grow"); prompt.add(new JLabel("Picks"), "align left, grow"); prompt.add(pksInput, "align right, grow, wrap"); prompt.add(new JLabel("Cut #"), "align right, grow"); prompt.add(cut1Input, "align left, grow"); prompt.add(new JLabel("Cut Suffix"), "align right, grow"); prompt.add(cut2Input, "align left, grow, wrap"); int option = JOptionPane.showOptionDialog(self, prompt, "Add Sample Cut - Loom " + record.getLoomNumber(), JOptionPane.NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{ "Add", "Cancel" }, "Add"); if(option == 0){ int cutNumber = 0; double sampHours = 0; int sampPicks = 0; String cutSuffix = ""; if(cut2Input.getText().length() > 2) cutSuffix = cut2Input.getText().substring(0, 2).toUpperCase(); else cutSuffix = cut2Input.getText().toUpperCase(); try{ if(hrsInput.getText().startsWith("m")) { double minutes = (double) Integer.parseInt(hrsInput.getText().substring(1)); sampHours = round(minutes / 60.0, 2); } else { sampHours = Double.parseDouble(hrsInput.getText()); } sampPicks = Integer.parseInt(pksInput.getText()); cutNumber = Integer.parseInt(cut1Input.getText()); if(sampHours <= 0) throw new NumberFormatException("User passed a negative/zero hours value. Aint nobody got time fo dat."); else if(sampHours > hrsBackup) throw new NumberFormatException("Not enough hours to cut in sample! Orig Record: " + hrsBackup + " User Entered: " + sampHours); if(sampPicks <= 0) throw new NumberFormatException("User passed a negative/zero picks value. Aint nobody got time fo dat."); else if(sampPicks > pksBackup) throw new NumberFormatException("Not enough picks to cut in sample! Orig Record: " + pksBackup + " User Entered: " + sampPicks); if(cutNumber < 1 || cutNumber > 999) throw new NumberFormatException("Invalid cut number: " + cutNumber + ". Valid range [1 - 999]"); record.setHours(hrsBackup - sampHours); record.setPicks(Math.min((pksBackup - sampPicks), record.getMaxPicks())); //By adjusting the hours first on the previous line; getMaxPicks() will return the new maximum picks that we do not wish to exceed //record.setPicks(pksBackup - sampPicks); //record.setHours(hrsBackup - sampHours); } catch(NumberFormatException e2){ JOptionPane.showMessageDialog(self, "Non-negative decimal values please!\n\rNo changes made.", "Bogus Input", JOptionPane.ERROR_MESSAGE); LOGGER.log(Level.SEVERE, "User passed us bogus input. Ignoring..", e2); return; } try { as400.adjustHoursPicks(record); //Decrease hours and picks of target record as400.addSampleCut(record, cutNumber, cutSuffix, sampHours, sampPicks); //Add new SAM record to LOOMDS with desired hours/picks reloadTab(key); } catch (SQLException e1) { LOGGER.log(Level.SEVERE, "Database transaction failed when attempting to cut in sample. Reverting Changes.", e1); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rSomething went wrong when applying this change to the AS400", "Update Failed", JOptionPane.ERROR_MESSAGE); record.setHours(hrsBackup); //Revert hours in LoomDSRecord object to previous value since DB transaction failed record.setPicks(pksBackup); //Revert picks in LoomDSRecord object to previous value since DB transaction failed } catch (ParseException e1) { LOGGER.log(Level.WARNING, "Failed to reload shift detail after record insert. Re-open the tab manually", e1); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rFailed to reload shift detail after record insert\n\rRe-open tab manually", "Whoops!", JOptionPane.WARNING_MESSAGE); } } } }); delete.addActionListener(new ActionListener() { private final Logger LOGGER = Logger.getLogger(LoomDSRecord.class.getName()); @Override public void actionPerformed(ActionEvent e) { String msg = "Are you absolutely sure you want to DELETE this record?
This _CANNOT_ be un-done!
"; msg = msg + "Loom: " + record.getLoomNumber() + "
"; msg = msg + "Weaver: " + record.getWeaverNameDisplay() + "
"; msg = msg + "Style: " + record.getLdStyle() + "
"; msg = msg + "Hours: " + record.getHours() + "
"; msg = msg + "Picks: " + record.getPicks() + "
"; msg = msg + "Down Time: " + record.getformattedDowntimeReason() + "
"; msg = msg + "Cut: " + record.getFormattedCutNumber() + "
"; msg = msg + "Type Code: " + record.getTypeCode(); int proceed = JOptionPane.showConfirmDialog(self, msg, "Delete Record", JOptionPane.OK_CANCEL_OPTION); if(proceed == 0){ try { as400.delRecord(record); reloadTab(key); } catch (SQLException e1) { LOGGER.log(Level.SEVERE, "Database transaction failed when attempting to delete record..", e1); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rSomething went wrong when applying this change to the AS400", "Update Failed", JOptionPane.ERROR_MESSAGE); } catch (ParseException e1) { LOGGER.log(Level.WARNING, "Failed to reload shift detail after record deletion. Re-open the tab manually", e1); JOptionPane.showMessageDialog(self, "Uh-oh..\n\rFailed to reload shift detail after record deletion\n\rRe-open tab manually", "Whoops!", JOptionPane.WARNING_MESSAGE); } } } }); ret.add(adjPicks); ret.add(adjHours); ret.addSeparator(); ret.add(addSamp); ret.addSeparator(); ret.add(chgWeav); ret.add(setDT); ret.addSeparator(); ret.add(delete); return ret; } }