package com.vistatec.ocelot.lqi.gui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.TableColumnModelEvent; import javax.swing.event.TableColumnModelListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vistatec.ocelot.PlatformSupport; import com.vistatec.ocelot.config.ConfigTransferService.TransferException; import com.vistatec.ocelot.lqi.LQIGridController; import com.vistatec.ocelot.lqi.LQIKeyEventHandler; import com.vistatec.ocelot.lqi.LQIKeyEventManager; import com.vistatec.ocelot.lqi.model.LQIErrorCategory; import com.vistatec.ocelot.lqi.model.LQIGrid; import com.vistatec.ocelot.lqi.model.LQISeverity; import com.vistatec.ocelot.lqi.model.LQIShortCut; /** * Dialog displaying the LQI grid. */ public class LQIGridDialog extends JDialog implements ActionListener, Runnable { /** The serial version UID. */ private static final long serialVersionUID = -8889391524131724874L; /** Issues annotations mode constant. */ public static final int ISSUES_ANNOTS_MODE = 0; /** Configuration mode constant. */ public static final int CONFIG_MODE = 1; /** Dialog width padding. */ private static final int DIALOG_WIDTH_PADDING = 50; /** Dialog title. */ private static final String TITLE = "Quality Issues Grid"; /** Dialog title suffix displayed in Configuration mode. */ private static final String TITLE_CONF_SUFFIX = " - Configuration"; /** The logger for this class. */ private static final Logger logger = LoggerFactory.getLogger(LQIGridDialog.class); /** * Dialog mode. It can be set to either <code>ISSUES_ANNOTS_MODE</code> or * <code>CONFIG_MODE</code> value. */ private int mode; /** the LQI grid object. */ private LQIGrid lqiGrid; /** Configure button. */ private JButton btnConfig; /** Close button. */ private JButton btnClose; /** Save button. */ private JButton btnSave; /** Cancel button. */ private JButton btnCancel; /** Add new row button. */ private JButton btnAddRow; /** Delete row button. */ private JButton btnDeleteRow; /** Add new column button. */ private JButton btnAddCol; /** Arrow up button. */ private JButton btnUp; /** Arrow down button. */ private JButton btnDown; /** The columns panel. */ private JPanel colsPanel; /** The rows panel. */ private JPanel rowsPanel; /** The grid scroll panel. */ private JScrollPane scrollPane; /** The controller for this dialog. */ private LQIGridController controller; /** The helper object for table management. */ private LQIGridTableHelper tableHelper; /** The key event handler. */ private LQIKeyEventHandler lqiGridKeyEventHandler; private PlatformSupport platformSupport; /** * Constructor. * * @param owner * the owner window. * @param controller * the controller. * @param lqiGrid * the LQI grid object. */ public LQIGridDialog(JFrame owner, LQIGridController controller, LQIGrid lqiGrid, PlatformSupport platformSupport) { this(owner, controller, lqiGrid, ISSUES_ANNOTS_MODE, platformSupport); } /** * Constructor. * * @param owner * the owner window. * @param controller * the controller. * @param lqiGrid * the LQI grid object. * @param mode * the dialog mode to set */ public LQIGridDialog(Window owner, LQIGridController controller, LQIGrid lqiGrid, int mode, PlatformSupport platformSupport) { super(owner); setModal(false); this.lqiGrid = lqiGrid; this.controller = controller; this.mode = mode; this.platformSupport = platformSupport; } /** * Initializes the dialog. */ private void init() { lqiGridKeyEventHandler = new LQIKeyEventHandler(controller, getRootPane()); LQIKeyEventManager.getInstance().addKeyEventHandler( lqiGridKeyEventHandler); lqiGridKeyEventHandler.load(lqiGrid); tableHelper = new LQIGridTableHelper(platformSupport); String title = TITLE; if (mode == CONFIG_MODE) { title = title + TITLE_CONF_SUFFIX; } setTitle(title); setResizable(false); add(getCenterComponent(), BorderLayout.CENTER); add(getBottomComponent(), BorderLayout.SOUTH); setTableSize(); setLocationRelativeTo(getOwner()); } /** * Gets the component to be displayed in the center of the dialog. * * @return the component to be displayed in the center of the dialog. */ private Component getCenterComponent() { LQIGrid clonedGrid = null; try { clonedGrid = (LQIGrid) lqiGrid.clone(); } catch (CloneNotSupportedException e) { // never happens } scrollPane = new JScrollPane(tableHelper.createLQIGridTable(clonedGrid, mode, getGridButtonAction())); tableHelper.getLqiTable().addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { setTableSize(); } }); tableHelper.getLqiTable().getColumnModel() .addColumnModelListener(new TableColumnModelListener() { /** number of columns added so far. */ private int colAddedNum; @Override public void columnSelectionChanged(ListSelectionEvent arg0) { // do nothing } @Override public void columnRemoved(TableColumnModelEvent arg0) { // do nothing } @Override public void columnMoved(TableColumnModelEvent arg0) { // do nothing } @Override public void columnMarginChanged(ChangeEvent e) { // do nothing } @Override public void columnAdded(TableColumnModelEvent e) { colAddedNum++; if (colAddedNum == tableHelper.getLqiTableModel() .getColumnCount()) { colAddedNum = 0; setTableSize(); } } }); return scrollPane; } /** * Sets the table size. */ private synchronized void setTableSize() { int height = tableHelper.getLqiTable().getTableHeader().getSize().height + 5 + (tableHelper.getLqiTable().getRowHeight() * tableHelper .getLqiTable().getRowCount()); int width = DIALOG_WIDTH_PADDING + tableHelper.getTableWidth(); scrollPane.setPreferredSize(new Dimension(width, height)); tableHelper.initColorForColumns(); tableHelper.configureTable(getGridButtonAction()); pack(); } /** * Gets the action for the LQI grid buttons. * * @return the action for the LQI grid buttons. */ private Action getGridButtonAction() { Action annotationAncion = new AbstractAction() { private static final long serialVersionUID = -57792331199984334L; @Override public void actionPerformed(ActionEvent e) { LQIGridButton button = (LQIGridButton) e.getSource(); if (mode == ISSUES_ANNOTS_MODE) { int severityIndex = button.getSeverityColumn() - tableHelper.getLqiTableModel() .getSeverityColsStartIndex(); double severity = lqiGrid.getSeverities() .get(severityIndex).getScore(); String severityName = lqiGrid.getSeverities() .get(severityIndex).getName(); String categoryName = tableHelper .getLqiTable() .getValueAt( button.getCategoryRow(), tableHelper.getLqiTableModel() .getErrorCategoryColumn()) .toString(); controller.createNewLqi(categoryName, severity, severityName); } else if (mode == CONFIG_MODE) { LQIErrorCategory selErrorCat = tableHelper .getLqiTableModel().getErrorCategoryAtRow( tableHelper.getLqiTable().getSelectedRow()); if (selErrorCat != null) { int selCol = tableHelper.getLqiTable() .getSelectedColumn(); String severityName = tableHelper.getLqiTableModel() .getSeverityNameForColumn(selCol); LQIShortCut currShortcut = selErrorCat .getShortcut(severityName); ShortCutDialog shortCutDialog = new ShortCutDialog( LQIGridDialog.this, selErrorCat.getName() + " - " + severityName, currShortcut); shortCutDialog.run(); } } if (tableHelper.getLqiTable().getCellEditor() != null) { tableHelper.getLqiTable().getCellEditor().stopCellEditing(); LQIGridDialog.this.requestFocus(); } } }; return annotationAncion; } /** * Checks if the shortcut defined by the key code and the modifiers is a * reserved one. * * @param keyCode * the key code * @param modifiers * the modifiers * @return <code>true</code> if it is a reserved shortcut; * <code>false</code> otherwise. */ public boolean isReservedShortcut(int keyCode, int[] modifiers) { return tableHelper.isReservedShortcut(keyCode, modifiers); } /** * Clears the commnet cell for a specific category. * * @param category * the category name. */ public void clearCommentCellForCategory(String category) { tableHelper.getLqiTableModel().clearCommentForCategory(category); } /** * Gets the component to be displayed at the bottom of the dialog. * * @return the component to be displayed at the bottom of the dialog. */ private Component getBottomComponent() { JPanel bottomPanel = new JPanel(); bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS)); createAndConfigureButtons(); rowsPanel = new JPanel(); rowsPanel.setLayout(new BoxLayout(rowsPanel, BoxLayout.X_AXIS)); rowsPanel.setBorder(BorderFactory.createTitledBorder("Category rows")); rowsPanel.add(btnAddRow); rowsPanel.add(Box.createHorizontalStrut(10)); rowsPanel.add(btnDeleteRow); rowsPanel.add(Box.createHorizontalStrut(10)); rowsPanel.add(btnUp); rowsPanel.add(Box.createHorizontalStrut(10)); rowsPanel.add(btnDown); colsPanel = new JPanel(); colsPanel.setLayout(new BoxLayout(colsPanel, BoxLayout.X_AXIS)); colsPanel.setPreferredSize(new Dimension(120, 50)); colsPanel.setBorder(BorderFactory .createTitledBorder("Severity Columns")); colsPanel.add(btnAddCol); bottomPanel.add(Box.createRigidArea(new Dimension(1, 50))); bottomPanel.add(Box.createHorizontalStrut(10)); bottomPanel.add(btnConfig); bottomPanel.add(rowsPanel); bottomPanel.add(Box.createHorizontalStrut(10)); bottomPanel.add(colsPanel); bottomPanel.add(Box.createHorizontalGlue()); bottomPanel.add(btnSave); bottomPanel.add(Box.createHorizontalStrut(10)); bottomPanel.add(btnCancel); bottomPanel.add(btnClose); bottomPanel.add(Box.createHorizontalStrut(10)); if (mode == ISSUES_ANNOTS_MODE) { btnCancel.setVisible(false); btnSave.setVisible(false); colsPanel.setVisible(false); rowsPanel.setVisible(false); } else { btnConfig.setVisible(false); btnClose.setVisible(false); } return bottomPanel; } /** * Creates and configures all the buttons for this dialog. */ private void createAndConfigureButtons() { btnConfig = new JButton("Configure"); btnConfig.addActionListener(this); btnClose = new JButton("Close"); btnClose.addActionListener(this); btnCancel = new JButton("Cancel"); btnCancel.addActionListener(this); btnSave = new JButton("Save"); btnSave.addActionListener(this); Toolkit kit = Toolkit.getDefaultToolkit(); ImageIcon icon = new ImageIcon(kit.createImage(getClass().getResource( "add.png"))); btnAddRow = new JButton(icon); configIconButton(btnAddRow); btnAddCol = new JButton(icon); configIconButton(btnAddCol); icon = new ImageIcon(kit.createImage(getClass().getResource( "remove.png"))); btnDeleteRow = new JButton(icon); configIconButton(btnDeleteRow); icon = new ImageIcon(kit.createImage(getClass().getResource( "arrow-up.png"))); btnUp = new JButton(icon); configIconButton(btnUp); icon = new ImageIcon(kit.createImage(getClass().getResource( "arrow-down.png"))); btnDown = new JButton(icon); configIconButton(btnDown); } /** * Configures an icon button. * * @param btn * the button. */ private void configIconButton(JButton btn) { btn.setPreferredSize(new Dimension(20, 20)); btn.setOpaque(false); btn.setBorderPainted(false); btn.setContentAreaFilled(false); btn.addActionListener(this); } /** * Switches to the Issues Annotation mode. */ private void switchToIssuesAnnotsMode() { btnCancel.setVisible(false); btnSave.setVisible(false); rowsPanel.setVisible(false); colsPanel.setVisible(false); btnClose.setVisible(true); btnConfig.setVisible(true); setTitle(TITLE); repaint(); mode = ISSUES_ANNOTS_MODE; tableHelper.getLqiTableModel().setMode(mode); tableHelper.configureTable(getGridButtonAction()); setTableSize(); } /** * Switches to the configuration mode. */ private void switchToConfigMode() { btnCancel.setVisible(true); btnSave.setVisible(true); rowsPanel.setVisible(true); colsPanel.setVisible(true); btnClose.setVisible(false); btnConfig.setVisible(false); setTitle(TITLE + TITLE_CONF_SUFFIX); repaint(); mode = CONFIG_MODE; tableHelper.getLqiTableModel().setMode(mode); tableHelper.configureTable(getGridButtonAction()); setTableSize(); } /** * Saves the new LQI grid configuration. */ private void saveConfiguration() { try { if (tableHelper.getLqiTableModel().isChanged()) { controller.saveLQIGridConfiguration(tableHelper .getLqiTableModel().getLQIGrid()); lqiGrid = (LQIGrid) tableHelper.getLqiTableModel().getLQIGrid() .clone(); tableHelper.getLqiTableModel().setChanged(false); } switchToIssuesAnnotsMode(); } catch (TransferException e) { logger.error("Error while saving the LQI grid configuration.", e); JOptionPane .showMessageDialog( this, "An error has occurred while saving the LQI grid configuration", "LQI Grid Error", JOptionPane.ERROR_MESSAGE); } catch (CloneNotSupportedException e) { // never happens } } /** * Discards the changes applied to the LQI grid configuration. */ private void discardConfiguration() { boolean canSwitchMode = true; if (tableHelper.getLqiTableModel().isChanged()) { int option = JOptionPane .showConfirmDialog( this, "All changes to the grid will be discarded. Do you wish to continue?", "LQI Grid Discard Changes", JOptionPane.YES_NO_OPTION); if (option == JOptionPane.YES_OPTION) { try { tableHelper.replaceConfiguration((LQIGrid) lqiGrid.clone()); } catch (CloneNotSupportedException e) { // never happens } } else { canSwitchMode = false; } } if (canSwitchMode) { switchToIssuesAnnotsMode(); } } /* * (non-Javadoc) * * @see * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ @Override public void actionPerformed(ActionEvent e) { if (e.getSource().equals(btnConfig)) { switchToConfigMode(); } else if (e.getSource().equals(btnSave)) { saveConfiguration(); } else if (e.getSource().equals(btnCancel)) { discardConfiguration(); } else if (e.getSource().equals(btnClose)) { close(); } else if (e.getSource().equals(btnAddRow)) { addErrorCategory(); } else if (e.getSource().equals(btnDeleteRow)) { deleteSelectedErrorCategory(); } else if (e.getSource().equals(btnAddCol)) { addSeverity(); } else if (e.getSource().equals(btnUp)) { tableHelper.moveSelectedRowUp(); } else if (e.getSource().equals(btnDown)) { tableHelper.moveSelectedRowDown(); } } /** * Adds a severity column. */ private void addSeverity() { tableHelper.displayNewSeverityDialog(); } /** * Checks if an issue can be created. * * @return <code>true</code> if an issue can be created; <code>false</code> * otherwise */ public boolean canCreateIssue() { return mode == ISSUES_ANNOTS_MODE && !isEditing(); } /** * Checks if there's a text field being edited in Ocelot. * * @return <code>true</code> if a text field exists being edited; * <code>false</code> otherwise. */ private boolean isEditing() { return tableHelper.getLqiTable().getEditingColumn() == tableHelper .getLqiTableModel().getCommentColumn() || controller.isOcelotEditing(); } /** * Adds a new error category (i.e. a new row in the LQI grid). */ private void addErrorCategory() { if (mode == CONFIG_MODE) { tableHelper.addErrorCategory(); } } /** * Deletes the selected error category. */ private void deleteSelectedErrorCategory() { if (mode == CONFIG_MODE) { tableHelper.deleteSelectedErrorCategory(); } } /** * Handles the event a severity score has been changed. * * @param oldSeverity * the old severity. * @param newSeverity * the new severity. */ public void severityChanged(LQISeverity oldSeverity, LQISeverity newSeverity) { tableHelper.severityColumnChanged(oldSeverity, newSeverity); } /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ @Override public void run() { init(); setVisible(true); } /** * Closes the dialog and disposes it. */ private void close() { lqiGridKeyEventHandler.removeActions(lqiGrid); setVisible(false); controller.close(); } /** * Save a new shortcut * * @param keyCode * the shortcut key code * @param modifiers * the modifiers. */ public void saveShortcut(int keyCode, int[] modifiers) { tableHelper.saveShortCut(keyCode, modifiers); } /** * Gets the comment for a specific category. * * @param errorCategory * the error category * @return the comment */ public String getCommentForCategory(String errorCategory) { return tableHelper.getLqiTableModel().getCommentByCategory( errorCategory); } /** * Creates a new severity column for the given severity. * * @param severity * the severity. */ public void createSeverityColumn(LQISeverity severity) { tableHelper.addSeverityColumn(severity); } /** * Checks if a name chosen for a severity is a valid one. A severity name is * valid if no other severity has the same name. * * @param severity * the severity * @param newSeverityName * the new name. * @return <code>true</code> if the new name is a valid one; * <code>false</code> otherwise */ public boolean checkSeverityName(LQISeverity severity, String newSeverityName) { boolean validName = true; LQIGrid modelGrid = tableHelper.getLqiTableModel().getLQIGrid(); if (modelGrid.getSeverities() != null) { for (LQISeverity sev : modelGrid.getSeverities()) { if (!sev.equals(severity) && sev.getName().equals(newSeverityName)) { validName = false; break; } } } return validName; } }