// Copyright 2011-2016 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.security.zynamics.binnavi.Gui.MainWindow.ProjectTree.Nodes; import com.google.common.base.Preconditions; import com.google.security.zynamics.binnavi.Gui.Actions.CActionProxy; import com.google.security.zynamics.binnavi.Gui.CTableSearcherHelper; import com.google.security.zynamics.binnavi.Gui.FilterPanel.CFilteredTable; import com.google.security.zynamics.binnavi.Gui.HotKeys; import com.google.security.zynamics.binnavi.Help.IHelpInformation; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.AbstractAction; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; /** * Base class for all tables to be displayed on the right side of the main window. * * @param <T> Type of the elements shown in the table. */ public abstract class CAbstractTreeTable<T> extends CFilteredTable<T> { /** * The project tree of the main window. */ private final JTree tree; /** * The raw table model of the table. Sorting is done in the super class. */ private final CAbstractTreeTableModel<T> treeTableModel; /** * Mouse listener that handles clicks on the table. */ private final InternalMouseListener mouseListener = new InternalMouseListener(); /** * Creates a new abstract tree table object. * * @param projectTree The project tree shown in the main window. * @param model The raw model that is responsible for the table layout. * @param helpInfo Provides context-sensitive information for the table. */ public CAbstractTreeTable(final JTree projectTree, final CAbstractTreeTableModel<T> model, final IHelpInformation helpInfo) { super(model, helpInfo); treeTableModel = Preconditions.checkNotNull(model, "IE01939: Model argument can't be null"); tree = Preconditions.checkNotNull(projectTree, "IE02343: Project tree argument can not be null"); addMouseListener(mouseListener); setDefaultRenderer(String.class, new CProjectTreeTableRenderer()); final InputMap windowImap = getInputMap(JComponent.WHEN_FOCUSED); windowImap.put(HotKeys.SEARCH_HK.getKeyStroke(), "SEARCH"); getActionMap().put("SEARCH", CActionProxy.proxy(new SearchAction())); windowImap.put(HotKeys.DELETE_HK.getKeyStroke(), "DELETE"); getActionMap().put("DELETE", CActionProxy.proxy(new DeleteAction())); updateUI(); } /** * Creates a popup menu depending on where the user clicked and shows that context menu in the * table. * * @param event The mouse event that was created when the user clicked. */ private void displayPopupMenu(final MouseEvent event) { final int selectedIndex = getSelectionIndex(event); if (selectedIndex != -1) { final JPopupMenu popupMenu = getPopupMenu(event.getX(), event.getY(), selectedIndex); if (popupMenu != null) { popupMenu.show(this, event.getX(), event.getY()); } } } /** * Uses information from a mouse event to determine what row was clicked. * * @param event The mouse event. * * @return The index of the row that was clicked or -1 if the row could not be determined. */ private int getSelectionIndex(final MouseEvent event) { return convertRowIndexToModel(rowAtPoint(event.getPoint())); } /** * Deletes the selected rows. */ protected void deleteRows() { } /** * Returns the parent window of the tree table. * * @return The parent window of the tree table. */ protected JFrame getParentWindow() { return (JFrame) SwingUtilities.getWindowAncestor(tree); } /** * Creates a table-specific popup menu. * * @param x The x coordinate where the user clicked. * @param y The y coordinate where the user clicked. * @param selectedIndex The index of the row where the user clicked. * * @return The popup menu to be shown or null if no popup menu should be shown. */ protected abstract JPopupMenu getPopupMenu(int x, int y, int selectedIndex); /** * Returns the project tree of the main window. * * @return The project tree of the main window. */ protected JTree getProjectTree() { return tree; } /** * Returns the normalized indices of the selected rows. * * @return The normalized indices of the selected rows. */ protected int[] getSortSelectedRows() { final int[] rows = getSelectedRows(); for (int i = 0; i < rows.length; i++) { rows[i] = convertRowIndexToModel(rows[i]); } return rows; } /** * Handles double-clicks on table rows. * * @param row The index of the row that was clicked. */ protected abstract void handleDoubleClick(int row); @Override protected boolean processKeyBinding(final KeyStroke keyStroke, final KeyEvent event, final int condition, final boolean pressed) { // turn off edit but still can cause actions if (event.getKeyCode() == KeyEvent.VK_DELETE) { putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); final boolean retvalue = super.processKeyBinding(keyStroke, event, condition, pressed); putClientProperty("JTable.autoStartsEdit", Boolean.TRUE); return retvalue; } return super.processKeyBinding(keyStroke, event, condition, pressed); } /** * Clean-up function. */ @Override public void dispose() { removeMouseListener(mouseListener); treeTableModel.delete(); } @Override public CAbstractTreeTableModel<T> getTreeTableModel() { return treeTableModel; } /** * Action class that handles the deletion of rows. */ private class DeleteAction extends AbstractAction { @Override public void actionPerformed(final ActionEvent event) { deleteRows(); } } /** * Listens on mouse events and handles right-clicks and double-clicks. */ private class InternalMouseListener extends MouseAdapter { @Override public void mouseClicked(final MouseEvent event) { if ((event.getButton() == MouseEvent.BUTTON1) && (event.getClickCount() == 2)) { handleDoubleClick(getSelectionIndex(event)); } } @Override public void mousePressed(final MouseEvent event) { if (event.isPopupTrigger()) { displayPopupMenu(event); } } @Override public void mouseReleased(final MouseEvent event) { if (event.isPopupTrigger()) { displayPopupMenu(event); } } } /** * Action class that handles table searching. */ private class SearchAction extends AbstractAction { @Override public void actionPerformed(final ActionEvent event) { CTableSearcherHelper.search(SwingUtilities.getWindowAncestor(CAbstractTreeTable.this), CAbstractTreeTable.this); } } }