package net.oni2.aeinstaller.gui.modtable; import java.awt.Desktop; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.ResourceBundle; import java.util.TreeSet; import java.util.Vector; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.JViewport; import javax.swing.ListSelectionModel; import javax.swing.RowSorter; import javax.swing.SortOrder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.RowSorterEvent; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableRowSorter; import net.oni2.aeinstaller.backend.Settings; import net.oni2.aeinstaller.backend.packages.Package; import net.oni2.aeinstaller.backend.packages.Type; import net.oni2.aeinstaller.gui.downloadwindow.Downloader; /** * @author Christian Illy */ public class ModTable extends JTable { private static final long serialVersionUID = 1L; private ResourceBundle bundle = ResourceBundle .getBundle("net.oni2.aeinstaller.localization.ModTable"); /** * @author Christian Illy */ public enum ETableContentType { /** * Table showing mods */ MODS, /** * Table showing tools */ TOOLS, /** * Table showing core packages */ CORE }; private HashSet modSelListeners = new HashSet(); private ModTableModel model; private TableRowSorter sorter; private ETableContentType contentType = ETableContentType.MODS; /** * Create a new ModTable * * @param contentType * Content to show */ public ModTable(ETableContentType contentType) { super(); this.contentType = contentType; setSelectionMode(ListSelectionModel.SINGLE_SELECTION); getSelectionModel().addListSelectionListener(this); addMouseListener(new MouseEventHandler()); addKeyListener(new KeyEventHandler()); // To get checkbox-cells with background of row ((JComponent) getDefaultRenderer(Boolean.class)).setOpaque(true); model = new ModTableModel(contentType); setModel(model); sorter = new TableRowSorter(model); setRowSorter(sorter); setFilter(null, 0, null, EApplyFilterTo.ALL); List sortKeys = new ArrayList(); int sortCol = Settings.getInstance().get("modSortColumn", 1); SortOrder sortOrder = SortOrder.valueOf(Settings.getInstance().get( "modSortOrder", "ASCENDING")); sortKeys.add(new RowSorter.SortKey(sortCol, sortOrder)); sorter.setSortKeys(sortKeys); for (int i = 0; i < model.getColumnCount(); i++) { model.setColumnConstraints(i, getColumnModel().getColumn(i)); } getTableHeader().addMouseListener(new HeaderMouseEventHandler()); if (contentType != ETableContentType.MODS) { getColumnModel().removeColumn(getColumnModel().getColumn(0)); } } @Override public String getToolTipText(MouseEvent e) { int r = rowAtPoint(e.getPoint()); int c = columnAtPoint(e.getPoint()); if (r >= 0 && r < getRowCount()) { int modelCol = convertColumnIndexToModel(c); if (modelCol == 4) { final Package mod = (Package) getValueAt(r, -1); String tt = ""; tt += String.format("%s: %s
", bundle.getString("state.installed"), bundle.getString((mod.isInstalled() ? "yes" : "no"))); tt += String.format( "%s: %s
", bundle.getString("state.updatable"), bundle.getString((mod.isLocalAvailable() && mod.isNewerAvailable() ? "yes" : "no"))); tt += String.format("%s: %s", bundle .getString("state.downloaded"), bundle.getString((mod .isLocalAvailable() ? "yes" : "no"))); return tt; } } return super.getToolTipText(e); } /** * @param listener * Listener to add */ public void addModSelectionListener(ModSelectionListener listener) { modSelListeners.add(listener); } /** * @param listener * Listener to remove */ public void removeModSelectionListener(ModSelectionListener listener) { modSelListeners.remove(listener); } private void notifyModSelectionListeners(Package m) { for (ModSelectionListener l : modSelListeners) { l.modSelectionChanged(this, m); } } /** * @param listener * Listener to add */ public void addDownloadSizeListener(ModInstallSelectionListener listener) { model.addDownloadSizeListener(listener); } /** * @param listener * Listener to remove */ public void removeDownloadSizeListener(ModInstallSelectionListener listener) { model.removeDownloadSizeListener(listener); } /** * Reload the nodes data after an update to the cache */ public void reloadData() { model.reloadData(); } /** * Revert the selection to the mods that are currently installed */ public void revertSelection() { model.revertSelection(); } /** * Reload the selection after a config was loaded * * @param config * Config to load */ public void reloadSelection(File config) { model.reloadSelection(config); } /** * @return Mods selected for installation */ public TreeSet getSelectedMods() { return model.getSelectedMods(); } /** * @param type * Type of mods to show (null for all) * @param downloadState * Show only: 0 = all, 1 = online, 2 = downloaded * @param filterString * String to filter on * @param filterTo * Fields to use string filter on */ public void setFilter(Type type, int downloadState, String filterString, EApplyFilterTo filterTo) { sorter.setRowFilter(new ModTableFilter(type, downloadState, filterString, filterTo, contentType == ETableContentType.CORE, false)); } @Override public void sorterChanged(RowSorterEvent evt) { super.sorterChanged(evt); if (evt.getType() == RowSorterEvent.Type.SORT_ORDER_CHANGED) { @SuppressWarnings("unchecked") RowSorter rs = (RowSorter) getRowSorter(); List keys = rs.getSortKeys(); if (keys.size() > 0) { int col = keys.get(0).getColumn(); SortOrder so = keys.get(0).getSortOrder(); Settings.getInstance().put("modSortColumn", col); Settings.getInstance().put("modSortOrder", so.toString()); } } } /** * Select/Unselect all currently visible items depending on the current * state of selection */ public void unSelectAll() { boolean isAll = true; for (int i = 0; i < getRowCount(); i++) { int modRow = convertRowIndexToModel(i); boolean inst = (Boolean) model.getValueAt(modRow, 0); if (!inst) { isAll = false; break; } } for (int i = 0; i < getRowCount(); i++) { int modRow = convertRowIndexToModel(i); model.setValueAt(!isAll, modRow, 0); } invalidate(); repaint(); } @Override public void valueChanged(ListSelectionEvent e) { super.valueChanged(e); int viewRow = getSelectedRow(); if (viewRow < 0) { notifyModSelectionListeners(null); } else { Package mod = (Package) getValueAt(viewRow, -1); notifyModSelectionListeners(mod); } } private class MouseEventHandler extends MouseAdapter { private void mouseEventProcessing(MouseEvent e) { int r = rowAtPoint(e.getPoint()); if (r >= 0 && r < getRowCount()) setRowSelectionInterval(r, r); else clearSelection(); int rowindex = getSelectedRow(); if (rowindex >= 0) { if (e.isPopupTrigger() && e.getComponent() instanceof JTable) { final Package mod = (Package) getValueAt(rowindex, -1); JPopupMenu popup = new JPopupMenu(); if (mod.isLocalAvailable()) { // Open package folder item JMenuItem openModFolder = new JMenuItem( bundle.getString("openModFolder.text")); openModFolder.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { try { Desktop.getDesktop().open( mod.getLocalPath()); } catch (IOException e) { e.printStackTrace(); } } }); popup.add(openModFolder); } if (mod.getUrl() != null) { // Open Depot page item JMenuItem openDepotPage = new JMenuItem( bundle.getString("openDepotPage.text")); openDepotPage.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { try { Desktop.getDesktop().browse(mod.getUrl()); } catch (IOException e) { e.printStackTrace(); } } }); popup.add(openDepotPage); } if (mod.getFile() != null) { // Download package JMenuItem downloadPackage = new JMenuItem( bundle.getString("downloadPackage.text")); downloadPackage.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { TreeSet toDo = new TreeSet(); TreeSet deps = new TreeSet(); toDo.add(mod); Downloader dl = new Downloader(toDo, deps); try { dl.setVisible(true); } finally { dl.dispose(); } invalidate(); repaint(); } }); popup.add(downloadPackage); } if (mod.isLocalAvailable() && contentType != ETableContentType.CORE) { // Delete package folder item JMenuItem deleteModFolder = new JMenuItem( bundle.getString("deletePackage.text")); deleteModFolder.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { if (mod.getNode() == null) { JOptionPane.showMessageDialog( null, bundle.getString("deletePackageLocalOnly.text"), bundle.getString("deletePackageLocalOnly.title"), JOptionPane.INFORMATION_MESSAGE); } else { int res = JOptionPane.showConfirmDialog( null, bundle.getString("deletePackageConfirm.text"), bundle.getString("deletePackageConfirm.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (res == JOptionPane.YES_OPTION) { mod.deleteLocalPackage(); invalidate(); repaint(); } } } }); popup.add(deleteModFolder); } if (popup.getSubElements().length > 0) popup.show(e.getComponent(), e.getX(), e.getY()); } } } @Override public void mousePressed(MouseEvent e) { mouseEventProcessing(e); } @Override public void mouseReleased(MouseEvent e) { mouseEventProcessing(e); } } private class KeyEventHandler extends KeyAdapter { private void goToRow(int row) { setRowSelectionInterval(row, row); JViewport viewport = (JViewport) getParent(); Rectangle rect = getCellRect(row, 0, true); Rectangle r2 = viewport.getVisibleRect(); scrollRectToVisible(new Rectangle(rect.x, rect.y, (int) r2.getWidth(), (int) r2.getHeight())); } @Override public void keyTyped(KeyEvent e) { super.keyTyped(e); if (e.getModifiers() == 0) { String key = String.valueOf(e.getKeyChar()).toLowerCase(); int row = getSelectedRow(); if (row == (getRowCount() - 1)) row = -1; for (int i = row + 1; i < getRowCount(); i++) { Package m = (Package) getValueAt(i, -1); if (m.getName().toLowerCase().startsWith(key)) { goToRow(i); return; } } if (row > 0) { for (int i = 0; i < row; i++) { Package m = (Package) getValueAt(i, -1); if (m.getName().toLowerCase().startsWith(key)) { goToRow(i); return; } } } } } } private class HeaderMouseEventHandler extends MouseAdapter { private Vector columns = new Vector(); private TableColumnModel tcm = getColumnModel(); public HeaderMouseEventHandler() { super(); for (int i = 1; i < tcm.getColumnCount(); i++) { columns.add(tcm.getColumn(i)); } for (int i = 1; i < columns.size(); i++) { TableColumn tc = columns.get(i); if (!Settings.getInstance().get( String.format("modShowColumn%02d", tc.getModelIndex()), true)) tcm.removeColumn(tc); } } private TableColumn getColumn(String name) { for (TableColumn tc : columns) { if (tc.getHeaderValue().equals(name)) { return tc; } } return null; } private int headerContains(TableColumn tc) { for (int col = 0; col < tcm.getColumnCount(); col++) { if (tcm.getColumn(col).equals(tc)) return col; } return -1; } private void mouseEventProcessing(MouseEvent e) { if (e.isPopupTrigger() && e.getComponent() instanceof JTableHeader) { JPopupMenu popup = new JPopupMenu(); ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JCheckBoxMenuItem itm = (JCheckBoxMenuItem) e .getSource(); TableColumn col = getColumn(itm.getText()); if (itm.isSelected()) { tcm.addColumn(col); for (int i = columns.indexOf(col) - 1; i >= 0; i--) { int pos = headerContains(columns.get(i)); if (pos >= 0) { tcm.moveColumn(tcm.getColumnCount() - 1, pos + 1); break; } } } else { tcm.removeColumn(col); } Settings.getInstance().put( String.format("modShowColumn%02d", col.getModelIndex()), itm.isSelected()); } }; for (int i = 1; i < columns.size(); i++) { JCheckBoxMenuItem itm = new JCheckBoxMenuItem( (String) columns.get(i).getHeaderValue()); itm.setSelected(headerContains(columns.get(i)) >= 0); itm.addActionListener(al); popup.add(itm); } if (popup.getSubElements().length > 0) popup.show(e.getComponent(), e.getX(), e.getY()); } } @Override public void mousePressed(MouseEvent e) { mouseEventProcessing(e); } @Override public void mouseReleased(MouseEvent e) { mouseEventProcessing(e); } } }