/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.framework.internal; import java.awt.Component; import java.util.Arrays; import java.util.Iterator; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JList; import javax.swing.JScrollPane; import javax.swing.ListModel; import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import org.eclipse.persistence.tools.workbench.framework.app.AbstractApplicationNode; import org.eclipse.persistence.tools.workbench.framework.app.ApplicationNode; import org.eclipse.persistence.tools.workbench.framework.app.ApplicationProblemContainer; import org.eclipse.persistence.tools.workbench.framework.app.GroupContainerDescription; import org.eclipse.persistence.tools.workbench.framework.app.IconBuilder; import org.eclipse.persistence.tools.workbench.framework.context.ApplicationContext; import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext; import org.eclipse.persistence.tools.workbench.framework.ui.view.TitledPropertiesPage; import org.eclipse.persistence.tools.workbench.framework.uitools.SwingComponentFactory; import org.eclipse.persistence.tools.workbench.uitools.app.ItemListListValueModelAdapter; import org.eclipse.persistence.tools.workbench.uitools.app.ItemStateListValueModelAdapter; import org.eclipse.persistence.tools.workbench.uitools.app.ListValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.SimpleListValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.ValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.swing.ListModelAdapter; import org.eclipse.persistence.tools.workbench.uitools.cell.DisplayableListCellRenderer; import org.eclipse.persistence.tools.workbench.utility.events.ListChangeEvent; import org.eclipse.persistence.tools.workbench.utility.events.ListChangeListener; import org.eclipse.persistence.tools.workbench.utility.io.IndentingPrintWriter; import org.eclipse.persistence.tools.workbench.utility.node.Problem; import org.eclipse.persistence.tools.workbench.utility.string.StringTools; /** * This is a pseudo-node that is "selected" whenever multiple nodes * on the tree are selected. It supplies a properties page that * simply lists the selected nodes in a list box. * * There is one multi-selection "pseudo-node" per workspace view. */ final class MultiSelectionPseudoNode extends AbstractApplicationNode { /** * The selected nodes are added and removed by the workspace * view, which is monitoring the navigator. */ private SimpleListValueModel selectedNodesHolder; /** cache the properties page so we don't have to rebuild it repeatedly */ private LocalPropertiesPage propertiesPage; // ************ constructor/initialization ************ /** * use the super-secret, framework-only constructor... */ MultiSelectionPseudoNode(ApplicationContext context) { super(context); } protected void initialize() { super.initialize(); this.selectedNodesHolder = new SimpleListValueModel(); ListValueModel lvm = new ItemListListValueModelAdapter(this.selectedNodesHolder, ApplicationProblemContainer.BRANCH_APPLICATION_PROBLEMS_LIST); lvm.addListChangeListener(ValueModel.VALUE, this.buildBranchApplicationProblemsListener()); } private ListChangeListener buildBranchApplicationProblemsListener() { return new ListChangeListener() { public void itemsAdded(ListChangeEvent e) { MultiSelectionPseudoNode.this.branchProblemsChanged(); } public void itemsRemoved(ListChangeEvent e) { MultiSelectionPseudoNode.this.branchProblemsChanged(); } public void itemsReplaced(ListChangeEvent e) { MultiSelectionPseudoNode.this.branchProblemsChanged(); } public void listChanged(ListChangeEvent e) { MultiSelectionPseudoNode.this.branchProblemsChanged(); } public String toString() { return StringTools.buildToStringFor(this, "branch app problems listener"); } }; } // ********** AbstractTreeNodeValueModel overrides ********** public boolean equals(Object o) { return this == o; } public int hashCode() { return System.identityHashCode(this); } // ********** AbstractApplicationNode overrides ********** /** * this node does not have a value; do not call this method * willy-nilly on a collection of heterogeneous nodes ~bjv */ public Object getValue() { throw new UnsupportedOperationException(); } protected String buildDisplayString() { return this.resourceRepository().getString("MULTI_SELECTION_DISPLAY_STRING"); } /** * we don't have an icon */ protected IconBuilder buildIconBuilder() { return IconBuilder.NULL_INSTANCE; } /** * we are never dirty */ protected boolean buildDirtyFlag() { return false; } /** * increase visibility slightly for listener */ protected void branchProblemsChanged() { super.branchProblemsChanged(); } /** * delegate to the selected nodes */ protected void addExclusiveApplicationProblemsTo(List list) { for (Iterator stream = this.selectedNodes(); stream.hasNext(); ) { ((ApplicationProblemContainer) stream.next()).addApplicationProblemsTo(list); } } /** * delegate to the selected nodes */ public void addBranchApplicationProblemsTo(List list) { for (Iterator stream = this.selectedNodes(); stream.hasNext(); ) { ((ApplicationProblemContainer) stream.next()).addBranchApplicationProblemsTo(list); } } /** * should never be called... */ public boolean containsBranchApplicationProblemFor(Problem problem) { throw new UnsupportedOperationException(); } public void printBranchApplicationProblemsOn(IndentingPrintWriter writer) { if (this.branchApplicationProblemsSize() == 0) { return; } for (Iterator stream = this.selectedNodes(); stream.hasNext(); ) { ((ApplicationProblemContainer) stream.next()).printBranchApplicationProblemsOn(writer); // recurse } } /** * the problems view will want to listen to our list of problems, * which is OK, but we don't have a value that we need to listen to; * our list of problems is driven by the list of selected nodes */ protected void engageValueBranchProblems() { // do nothing since we don't have a value } protected void disengageValueBranchProblems() { // do nothing since we don't have a value } /** * the properties page title label view will want to listen to our icon and text, * which is OK, but we don't have a value that we need to listen to; * the icon and text never change */ protected void engageValuePropertiesPageTitleIcon() { // nothing to engage this.rebuildPropertiesPageTitleIconBuilder(); this.rebuildPropertiesPageTitleIcon(); } protected void disengageValuePropertiesPageTitleIcon() { // nothing to disengage } protected void engageValuePropertiesPageTitleText() { // nothing to engage this.rebuildPropertiesPageTitleText(); } protected void disengageValuePropertiesPageTitleText() { // nothing to disengage } // ********** ApplicationNode implementation ********** public GroupContainerDescription buildMenuDescription(WorkbenchContext workbenchContext) { throw new UnsupportedOperationException(); } public GroupContainerDescription buildToolBarDescription(WorkbenchContext workbenchContext) { throw new UnsupportedOperationException(); } // ********** EditorNode implementation ********** /** * the workspace view should only have one multi-select node, * so we only need one properties page */ public Component propertiesPage(WorkbenchContext workbenchContext) { if (this.propertiesPage == null) { WorkbenchContext ctx = this.buildLocalWorkbenchContext(workbenchContext); this.propertiesPage = new LocalPropertiesPage(ctx); this.propertiesPage.setNode(this, ctx); } return this.propertiesPage; } public void releasePropertiesPage(Component page) { // do nothing } // ********** miscellaneous ********** /** * this is how the workspace view notifies us that * the selection has changed */ void setSelectedNodes(ApplicationNode[] newNodes) { this.selectedNodesHolder.clear(); this.selectedNodesHolder.addItems(0, Arrays.asList(newNodes)); } ListValueModel getSelectedNodesHolder() { return this.selectedNodesHolder; } private Iterator selectedNodes() { return (Iterator) this.selectedNodesHolder.getValue(); } public void toString(StringBuffer sb) { sb.append("[multiple nodes]"); } // ********** inner classes ********** /** * This is the properties page displayed when the user has selected * multiple nodes in the "navigator" tree. Unlike most properties pages, * this one is not shared among the workbench windows. There is * one multi-selection "pseudo" node and properties page per * workbench window. */ private class LocalPropertiesPage extends TitledPropertiesPage { LocalPropertiesPage(WorkbenchContext context) { super(context); } protected Component buildPage() { JList listBox = SwingComponentFactory.buildList(this.buildSelectedNodesListModel()); listBox.setBorder(BorderFactory.createEmptyBorder()); listBox.setCellRenderer(new DisplayableListCellRenderer()); listBox.setBackground(UIManager.getColor("Panel.background")); listBox.setForeground(UIManager.getColor("List.foreground")); listBox.setSelectionBackground(UIManager.getColor("ScrollPane.background")); listBox.setSelectionForeground(listBox.getForeground()); JScrollPane scrollPane = new JScrollPane(listBox); scrollPane.setBorder(new EmptyBorder(5, 0, 0, 0)); scrollPane.getVerticalScrollBar().setUnitIncrement(10); scrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); return scrollPane; } /** * keep the nodes in the same order as they appear in the navigator; * this also keeps them in the same order as their problems in * the ProblemsView */ private ListModel buildSelectedNodesListModel() { return new ListModelAdapter(this.buildStateChangeAdapter()); // use the following bit of code to sort the nodes by display string: // return new ListModelAdapter(new SortedListValueModelAdapter(this.buildStateChangeAdapter())); } private ListValueModel buildStateChangeAdapter() { return new ItemStateListValueModelAdapter(MultiSelectionPseudoNode.this.getSelectedNodesHolder()); } } }