/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.designer.core.editor.structuretree;
import java.util.TreeSet;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreePath;
import org.pentaho.reporting.designer.core.ReportDesignerContext;
import org.pentaho.reporting.designer.core.editor.ReportDocumentContext;
import org.pentaho.reporting.designer.core.model.selection.DocumentContextSelectionModel;
import org.pentaho.reporting.designer.core.model.selection.ReportSelectionEvent;
import org.pentaho.reporting.designer.core.model.selection.ReportSelectionListener;
public abstract class AbstractReportTree extends JTree
{
public static enum RenderType
{
REPORT, DATA
}
protected class RestoreStateTask implements Runnable
{
protected RestoreStateTask()
{
}
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p/>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
public void run()
{
if (getRenderContext() == null)
{
return;
}
for (final Integer index : getExpandedNodes())
{
expandRow(index.intValue());
}
}
}
private class ReportTreeExpansionListener implements TreeWillExpandListener
{
public void treeWillExpand(final TreeExpansionEvent event) throws ExpandVetoException
{
addExpandedNode(getRowForPath(event.getPath()));
}
public void treeWillCollapse(final TreeExpansionEvent event) throws ExpandVetoException
{
removeExpandedNode(getRowForPath(event.getPath()));
}
}
private class TreeSelectionHandler implements TreeSelectionListener
{
/**
* Called whenever the value of the selection changes.
*
* @param e the event that characterizes the change.
*/
public void valueChanged(final TreeSelectionEvent e)
{
addExpandedNode(getRowForPath(e.getPath()));
if (updateFromExternalSource)
{
return;
}
updateFromInternalSource = true;
try
{
final ReportDocumentContext renderContext = getRenderContext();
if (renderContext == null)
{
return;
}
final TreePath[] treePaths = getSelectionPaths();
if (treePaths == null)
{
selectionModel.clearSelection();
renderContext.getSelectionModel().clearSelection();
return;
}
final DocumentContextSelectionModel selectionModel = renderContext.getSelectionModel();
final Object[] data = new Object[treePaths.length];
for (int i = 0; i < treePaths.length; i++)
{
final TreePath path = treePaths[i];
data[i] = path.getLastPathComponent();
}
selectionModel.setSelectedElements(data);
}
finally
{
updateFromInternalSource = false;
}
}
}
private class ReportSelectionHandler implements ReportSelectionListener
{
public void selectionAdded(final ReportSelectionEvent event)
{
if (isUpdateFromInternalSource())
{
return;
}
try
{
setUpdateFromExternalSource(true);
final TreePath path = getPathForNode(event.getElement());
if (path != null)
{
addSelectionPath(path);
}
}
finally
{
setUpdateFromExternalSource(false);
}
}
public void selectionRemoved(final ReportSelectionEvent event)
{
if (isUpdateFromInternalSource())
{
return;
}
try
{
setUpdateFromExternalSource(true);
final TreePath path = getPathForNode(event.getElement());
if (path != null)
{
removeSelectionPath(path);
}
}
finally
{
setUpdateFromExternalSource(false);
}
}
public void leadSelectionChanged(final ReportSelectionEvent event)
{
}
}
protected static final DefaultTreeModel EMPTY_MODEL = new DefaultTreeModel(null, false);
private ReportDesignerContext reportDesignerContext;
private boolean updateFromInternalSource;
private boolean updateFromExternalSource;
private ReportSelectionHandler selectionHandler;
public AbstractReportTree()
{
super(EMPTY_MODEL);
selectionHandler = new ReportSelectionHandler();
addTreeWillExpandListener(new ReportTreeExpansionListener());
getSelectionModel().addTreeSelectionListener(new TreeSelectionHandler());
}
protected ReportSelectionHandler getSelectionHandler()
{
return selectionHandler;
}
protected abstract TreePath getPathForNode (Object node);
public abstract void setRenderContext(final ReportDocumentContext renderContext);
protected abstract ReportDocumentContext getRenderContext();
public ReportDesignerContext getReportDesignerContext()
{
return reportDesignerContext;
}
public void setReportDesignerContext(final ReportDesignerContext reportDesignerContext)
{
this.reportDesignerContext = reportDesignerContext;
}
protected TreeSet<Integer> getExpandedNodes()
{
final ReportDocumentContext renderContext = getRenderContext();
if (renderContext == null)
{
// dummy operation..
return new TreeSet<Integer>();
}
final Object property = renderContext.getProperties().get("::layout-report-tree:expanded-nodes");
if (property instanceof TreeSet)
{
return (TreeSet<Integer>) property;
}
final TreeSet<Integer> retval = new TreeSet<Integer>();
renderContext.getProperties().put("::layout-report-tree:expanded-nodes", retval);
return retval;
}
protected void addExpandedNode (final int row)
{
getExpandedNodes().add(row);
}
protected void removeExpandedNode (final int row)
{
getExpandedNodes().remove(row);
}
protected void restoreState()
{
SwingUtilities.invokeLater(new RestoreStateTask());
}
protected boolean isUpdateFromInternalSource()
{
return updateFromInternalSource;
}
protected void setUpdateFromInternalSource(final boolean updateFromInternalSource)
{
this.updateFromInternalSource = updateFromInternalSource;
}
protected boolean isUpdateFromExternalSource()
{
return updateFromExternalSource;
}
protected void setUpdateFromExternalSource(final boolean updateFromExternalSource)
{
this.updateFromExternalSource = updateFromExternalSource;
}
protected void invalidateLayoutCache()
{
// this bit of magic invalidates the layout cache
setCellRenderer(new StructureTreeCellRenderer());
}
}