Package com.intellij.jam.view.treetable

Source Code of com.intellij.jam.view.treetable.JamTreeTableView

/*
* Copyright 2000-2007 JetBrains s.r.o.
*
* 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.intellij.jam.view.treetable;

import com.intellij.ide.util.treeView.AbstractTreeBuilder;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.jam.view.tree.JamAbstractTreeBuilder;
import com.intellij.jam.view.tree.JamNodeDescriptor;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.dualView.TreeTableView;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.util.Function;
import com.intellij.util.Icons;
import com.intellij.util.PairProcessor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.SoftArrayHashMap;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.Tree;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.util.ui.treetable.ListTreeTableModelOnColumns;
import com.intellij.util.ui.treetable.TreeTableCellRenderer;
import com.intellij.util.ui.treetable.TreeTableModel;
import com.intellij.util.ui.treetable.TreeTableTree;
import com.intellij.util.xml.ui.CommittablePanel;
import com.intellij.util.xml.ui.EmptyPane;
import com.intellij.util.xml.ui.StripeTableCellRenderer;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.table.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.*;
import java.util.List;

/**
* @author peter
*/
public abstract class JamTreeTableView implements CommittablePanel, DataProvider {
  private static final DefaultTableCellRenderer LOADING_NODE_RENDERER = new DefaultTableCellRenderer() {
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
      final Component cellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
      setText("");
      return cellRendererComponent;
    }
  };
  @NonNls private static final String TREE = "tree";
  @NonNls private static final String EMPTY = "empty";
  private final CardLayout myCardLayout;
  private final JPanel myContentPanel = new JPanel();
  private final EmptyPane myEmptyPane = new EmptyPane("If you see this text, please, submit a bug");
  private final JPanel myPanel = new PanelToViewDataDelegator();
  private final TreeTableView myTreeTableView;

  private final JamAbstractTreeBuilder myBuilder;
  private final Project myProject;
  private final JamNodeDescriptor myRootDescriptor;
  private final ListTreeTableModelOnColumns myModel;
  private final SoftArrayHashMap<Object,List<Object>> myCache = new SoftArrayHashMap<Object, List<Object>>();
  private boolean myTreeShowing = false;

  public JamTreeTableView(final Project project, final JamNodeDescriptor rootDescriptor) {
    myProject = project;
    myRootDescriptor = rootDescriptor;

    final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
    myModel = new ListTreeTableModelOnColumns(rootNode, ColumnInfo.EMPTY_ARRAY) {

      public boolean isCellEditable(Object node, int column) {
        try {
          return super.isCellEditable(getJamNodeDescriptor(node), column);
        }
        catch (LoadingNodeException e) {
          return false;
        }
      }

      public void setValueAt(final Object aValue, Object node, int column) {
        try {
          final ColumnInfo columnInfo = getColumnInfos()[column];
          final JamNodeDescriptor descriptor = getJamNodeDescriptor(node);
          getCachedColumnValues(((DefaultMutableTreeNode) node).getUserObjectPath()).set(column, aValue);
          JamTreeTableView.this.setValueAt(columnInfo, descriptor, aValue);
        }
        catch (LoadingNodeException e) {
        }
      }

      public Object getValueAt(Object value, int column) {
        return getCachedColumnValues(((DefaultMutableTreeNode) value).getUserObjectPath()).get(column);
      }
    };
    myTreeTableView = new MyTreeTableView(myModel);

    myBuilder = new JamAbstractTreeBuilder(project, getTree(), myModel, rootDescriptor) {

      protected boolean updateNodeDescriptor(final NodeDescriptor descriptor) {
        final boolean result = super.updateNodeDescriptor(descriptor);
        if (!descriptor.equals(myRootDescriptor) || getTree().isRootVisible()) {
          if (((JamNodeDescriptor) descriptor).isValid()) {
            cacheNode((JamNodeDescriptor)descriptor);
          }
        }
        return result;
      }

      public boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) {
        return false;
      }
    };

    Disposer.register(this, myBuilder);

    myTreeTableView.setDragEnabled(false);
    myTreeTableView.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    myTreeTableView.setRowHeight(Icons.CLASS_ICON.getIconHeight());
    final JTableHeader header = myTreeTableView.getTableHeader();
    header.addMouseMotionListener(new MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        updateTooltip(e);
      }
    });
    header.addMouseListener(new MouseAdapter() {
      public void mouseEntered(MouseEvent e) {
        updateTooltip(e);
      }
    });
    header.setReorderingAllowed(false);

    getTree().setShowsRootHandles(true);
    UIUtil.setLineStyleAngled(getTree());
    getTree().setCellRenderer(new JamToolTipRenderer());
    getTree().getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

    EditSourceOnDoubleClickHandler.install(myTreeTableView);

    myBuilder.init();

    myPanel.setLayout(new BorderLayout());
    myPanel.add(myContentPanel, BorderLayout.CENTER);
    myCardLayout = new CardLayout();
    myContentPanel.setLayout(myCardLayout);
    myContentPanel.add(myEmptyPane.getComponent(), EMPTY);
    myContentPanel.add(ScrollPaneFactory.createScrollPane(myTreeTableView), TREE);
  }

  protected final void updateTooltip(final MouseEvent e) {
    final int i = myTreeTableView.columnAtPoint(e.getPoint());
    if (i >= 0) {
      myTreeTableView.getTableHeader().setToolTipText(getColumnInfos()[i].getTooltipText());
    }
  }


  protected void setValueAt(final ColumnInfo columnInfo, final JamNodeDescriptor descriptor, final Object aValue) {
    columnInfo.setValue(descriptor, aValue);
    reset();
  }

  protected final void init() {
    final JComponent toolbar = createToolbar();
    if (toolbar != null) {
      myPanel.add(toolbar, BorderLayout.NORTH);
    }
    final ActionGroup actionGroup = createActionGroup(true);
    if (actionGroup != null) {
      PopupHandler.installPopupHandler(myEmptyPane.getComponent(), actionGroup, ActionPlaces.J2EE_ATTRIBUTES_VIEW_POPUP, ActionManager.getInstance());
      PopupHandler.installPopupHandler(myTreeTableView, actionGroup, ActionPlaces.J2EE_ATTRIBUTES_VIEW_POPUP, ActionManager.getInstance());
    }
    reset();
    TreeUtil.expandAll(getTree());
  }

  public final TreeTableView getTreeTableView() {
    return myTreeTableView;
  }

  public final Project getProject() {
    return myProject;
  }

  public final Tree getTree() {
    return myTreeTableView.getTree();
  }

  public final void refreshTreeTable() {
    boolean showTree = isShowTree();
    final boolean visibilityChanged = showTree != myTreeShowing;
    if (visibilityChanged || showTree) {
      TreePath[] selection = myTreeTableView.getTree().getSelectionPaths();
      cacheValues();
      myBuilder.setWaiting(false);
      myBuilder.updateFromRoot();
      if (!getTree().isExpanded(0)) {
        TreeUtil.expandRootChildIfOnlyOne(getTree());
      }
      myTreeTableView.getSelectionModel().clearSelection();
      if (selection != null) {
        for (TreePath treePath : selection) {
          final int row = myTreeTableView.getTree().getRowForPath(treePath);
          myTreeTableView.getSelectionModel().addSelectionInterval(row, row);
        }
      }
    }
    if (!showTree) {
      //noinspection HardCodedStringLiteral
      myEmptyPane.setText("<html>" + getEmptyPaneText() + "</html>");
    }
    if (visibilityChanged) {
      myTreeShowing = showTree;
      if (showTree) {
        TreeUtil.expandRootChildIfOnlyOne(getTree());
        myCardLayout.show(myContentPanel, TREE);
      }
      else {
        myCardLayout.show(myContentPanel, EMPTY);
      }
      myContentPanel.requestFocus();
    }
  }

  protected void setColumnsPreferredWidth() {
    final ColumnInfo[] columnInfos = getColumnInfos();
    TableColumnModel columnModel = myTreeTableView.getColumnModel();
    final JTableHeader header = myTreeTableView.getTableHeader();
    final FontMetrics fontMetrics = header.getFontMetrics(header.getFont());
    int maxPreferredWidth = 0;
    TableColumn treeColumn = null;
    for (int i = 0; i < columnInfos.length; i++) {
      final ColumnInfo columnInfo = columnInfos[i];
      final TableColumn column = columnModel.getColumn(i);
      final String name = columnInfo.getName();
      final int minWidth = StringUtil.isNotEmpty(name.trim()) ? fontMetrics.stringWidth(name) + 15 : 0;
      column.setMinWidth(minWidth);
      if (TreeTableModel.class.isAssignableFrom(columnInfo.getColumnClass())) {
        treeColumn = column;
        final int preferredWidth = myTreeTableView.getTree().getSize().width;
        column.setPreferredWidth(preferredWidth);
        if (preferredWidth > minWidth) {
          column.setMinWidth(preferredWidth);
        }
      }
      else if (!(columnInfo instanceof JamSpacerColumnInfo)) {
        final int fixedWidth = columnInfo.getWidth(myTreeTableView);
        if (fixedWidth > 0) {
          final int realWidth = Math.max(minWidth, fixedWidth);
          column.setMaxWidth(realWidth);
          column.setMinWidth(realWidth);
          column.setPreferredWidth(realWidth);
        }
        else {
          int preferredWidth = minWidth;
          final String preferredValue = columnInfo.getPreferredStringValue();
          if (preferredValue != null) {
            preferredWidth = Math.max(preferredWidth, fontMetrics.stringWidth(preferredValue));
          }
          column.setPreferredWidth(preferredWidth + columnInfo.getAdditionalWidth());
          final String maxValue = columnInfo.getMaxStringValue();
          if (maxValue != null) {
            column.setMaxWidth(fontMetrics.stringWidth(preferredValue) + columnInfo.getAdditionalWidth());
          }
        }
      }
      maxPreferredWidth = Math.max(maxPreferredWidth, column.getPreferredWidth());
    }
    if (treeColumn != null) {
      if (treeColumn.getPreferredWidth() < 4 * maxPreferredWidth) {
        treeColumn.setPreferredWidth(4 * maxPreferredWidth);
      }
    }
  }

  @Nullable
  protected ActionGroup createActionGroup(final boolean isPopup) {
    return isPopup? createPopupActionGroup() : createToolbarActions();
  }

  @Nullable
  protected ActionGroup createPopupActionGroup() {
    return null;
  }

  @Nullable
  protected ActionGroup createToolbarActions() {
    return null;
  }

  @Nullable
  protected JComponent createToolbar() {
    final ActionGroup actionGroup = createActionGroup(false);
    if (actionGroup == null) return null;

    final JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.PROJECT_VIEW_TOOLBAR, actionGroup, true).getComponent();
    component.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.darkGray), component.getBorder()));
    return component;
  }

  public void dispose() {
  }

  public JComponent getComponent() {
    return myPanel;
  }

  public void reset() {
    final boolean columnsChanged = myModel.setColumns(createColumnInfos());
    refreshTreeTable();
    final AbstractTableModel tableModel = (AbstractTableModel)myTreeTableView.getModel();
    if (columnsChanged) {
      tableModel.fireTableStructureChanged();
      setColumnsPreferredWidth();
    }
    if (myTreeShowing) {
      tableModel.fireTableDataChanged();
    }
  }

  public void commit() {
  }

  protected void cacheValues() {
    myCache.clear();
  }

  protected void recacheColumn(final int columnIndex) {
    assert columnIndex >= 0 && columnIndex < getColumnInfos().length;
    final ColumnInfo columnInfo = getColumnInfos()[columnIndex];
    myCache.processLeafEntries(new PairProcessor<Object, List<Object>>() {
      public boolean process(final Object o, final List<Object> objects) {
        objects.set(columnIndex, columnInfo.valueOf(o));
        return true;
      }
    });
  }

  private static JamNodeDescriptor getJamNodeDescriptor(final Object nodeDescriptor) throws LoadingNodeException{
    final DefaultMutableTreeNode node = (DefaultMutableTreeNode)nodeDescriptor;
    if (AbstractTreeBuilder.isLoadingNode(node)) {
      throw new LoadingNodeException();
    }

    return (JamNodeDescriptor)node.getUserObject();
  }

  private static class LoadingNodeException extends Exception {

  }

  protected final void setCachedValue(Object value, int column, Object... path) {
    final JamNodeDescriptor[] path1 = getDescriptorPath(path);
    if (path1 == null) return;
    getCachedColumnValues(path1).set(column, value);
  }

  private static JamNodeDescriptor[] getDescriptorPath(JamNodeDescriptor descriptor) {
    LinkedList<JamNodeDescriptor> list = new LinkedList<JamNodeDescriptor>();
    while (descriptor != null) {
      list.addFirst(descriptor);
      descriptor = (JamNodeDescriptor)descriptor.getParent();
    }
    return list.toArray(new JamNodeDescriptor[list.size()]);
  }

  @Nullable
  private JamNodeDescriptor[] getDescriptorPath(final Object... path) {
    assert corresponds(myRootDescriptor, path[0]);
    JamNodeDescriptor[] result = new JamNodeDescriptor[path.length];
    result[0] = myRootDescriptor;
    DefaultMutableTreeNode parent = myBuilder.getRootNode();
    for (int i = 1; i < path.length; i++) {
      final Object pathElement = path[i];
      final DefaultMutableTreeNode treeNode = parent == null ? null : myBuilder.getNodeForElement(pathElement);
      if (treeNode == null || !parent.equals(treeNode.getParent())) {
        result[i] = ContainerUtil.find(result[i - 1].getChildren(), new Condition<JamNodeDescriptor>() {
          public boolean value(final JamNodeDescriptor object) {
            return corresponds(object, pathElement);
          }
        });
        parent = treeNode;
      }
      else {
        result[i] = (JamNodeDescriptor)treeNode.getUserObject();
        parent = null;
      }
      if (result[i] == null) {
        return null;
      }
    }
    return result;
  }

  private static boolean corresponds(final JamNodeDescriptor object, final Object pathElement) {
    return Comparing.equal(object.getElement(), pathElement);
  }


  private List<Object> getCachedColumnValues(final Object[] path) {
    if (!myCache.containsKey(path)) {
      myCache.put(path, new ArrayList<Object>(getEmptyColumnValues()));
    }

    return myCache.get(path);
  }

  protected void cacheNode(final JamNodeDescriptor node) {
    final JamNodeDescriptor[] path = getDescriptorPath(node);
    if (!myCache.containsKey(path)) {
      myCache.put(path, getColumnValues(node));
    }
  }

  private List<Object> getColumnValues(final JamNodeDescriptor descriptor) {
    return ContainerUtil.map2List(getColumnInfos(), new Function<ColumnInfo, Object>() {
      public Object fun(final ColumnInfo s) {
        return s.valueOf(descriptor);
      }
    });
  }

  protected List<Object> getEmptyColumnValues() {
    return Arrays.asList(new Object[getColumnInfos().length]);
  }

  protected ColumnInfo[] getColumnInfos() {
    return myModel.getColumnInfos();
  }

  protected abstract boolean isShowTree();

  @NotNull
  protected String getEmptyPaneText() {
    return "";
  }

  protected abstract ColumnInfo[] createColumnInfos();

  @Nullable
  private Object getSelectedElement() {
    NodeDescriptor descriptor = getSelectedDescriptor();
    return descriptor == null ? null : descriptor.getElement();
  }

  @Nullable
  public final NodeDescriptor getSelectedDescriptor() {
    final TreePath path = getSelectedPath();
    if (path != null) {
      Object lastPathComponent = path.getLastPathComponent();
      if (lastPathComponent instanceof DefaultMutableTreeNode) {
        try {
          return getJamNodeDescriptor(lastPathComponent);
        }
        catch (LoadingNodeException e) {
        }
      }
    }
    return null;
  }

  @Nullable
  private TreePath getSelectedPath() {
    final TreePath[] paths = getTree() == null ? null : getTree().getSelectionPaths();
    return paths != null && paths.length == 1 ? paths[0] : null;
  }

  public Object getData(String dataId) {
    if (DataConstants.PSI_ELEMENT.equals(dataId)) {
      Object element = getSelectedElement();
      return element instanceof PsiElement && ((PsiElement)element).isValid() ? element : null;
    }
    else if (DataConstants.PSI_FILE.equals(dataId)) {
      Object element = getSelectedElement();
      return element instanceof PsiFile ? element : null;
    }
    else if (DataConstants.PROJECT.equals(dataId)) {
      return myProject;
    }
    NodeDescriptor descriptor = getSelectedDescriptor();
    if (descriptor instanceof JamNodeDescriptor) {
      return ((JamNodeDescriptor)descriptor).getDataForElement(dataId);
    }
    return null;
  }

  protected void setCachedValues(final Object value, final Collection<Integer> columns, final Object... url) {
    for (final int column : columns) {
      setCachedValue(value, column, url);
    }
  }

  private class PanelToViewDataDelegator extends JPanel implements DataProvider {
    public Object getData(String dataId) {
      return JamTreeTableView.this.getData(dataId);
    }
  }

  private static class MyTreeTableView extends TreeTableView {

    public MyTreeTableView(final ListTreeTableModelOnColumns model) {
      super(model);
    }

    public TreeTableCellRenderer createTableRenderer(TreeTableModel treeTableModel) {
      final TreeTableCellRenderer tableRenderer = super.createTableRenderer(treeTableModel);
      tableRenderer.setDefaultBorder(null);
      final TreeTableTree tree = getTree();
      return new TreeTableCellRenderer(this, tree) {
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
          final Component component1 = tableRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
          tree.setCellFocused(true);
          return component1;
        }
      };
    }

    protected final Object getRowElement(final int row) {
      try {
        return getJamNodeDescriptor(super.getRowElement(row));
      }
      catch (LoadingNodeException e) {
        return null;
      }
    }

    public TableCellRenderer getCellRenderer(int row, int column) {
      try {
        final JamNodeDescriptor node = getJamNodeDescriptor(super.getRowElement(row));
        return getColumnInfo(column).getCustomizedRenderer(node, new StripeTableCellRenderer(super.getCellRenderer(row, column)));
      }
      catch (LoadingNodeException e) {
        return LOADING_NODE_RENDERER;
      }
    }

  }
}
TOP

Related Classes of com.intellij.jam.view.treetable.JamTreeTableView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.