blob: a6c1dfa5cff10bdb2c9c552302dbfea409decf1e [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2004 IBM Corporation and others. All rights reserved. This
* program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and
* is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.wst.xml.ui.internal.tabletree;
import java.util.List;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.PlatformUI;
import org.eclipse.wst.common.contentmodel.modelquery.ModelQuery;
import org.eclipse.wst.sse.core.IModelStateListener;
import org.eclipse.wst.sse.core.IStructuredModel;
import org.eclipse.wst.sse.ui.ViewerSelectionManager;
import org.eclipse.wst.sse.ui.view.events.INodeSelectionListener;
import org.eclipse.wst.sse.ui.view.events.NodeSelectionChangedEvent;
import org.eclipse.wst.xml.core.document.XMLModel;
import org.eclipse.wst.xml.core.modelquery.ModelQueryUtil;
import org.eclipse.wst.xml.internal.ui.IDesignViewer;
import org.eclipse.wst.xml.internal.ui.XMLEditorResourceHandler;
import org.eclipse.wst.xml.ui.actions.NodeAction;
import org.eclipse.wst.xml.ui.dnd.XMLDragAndDropManager;
import org.eclipse.wst.xml.ui.views.contentoutline.XMLNodeActionManager;
import org.w3c.dom.Document;
public class XMLTableTreeViewer extends TreeViewer implements IDesignViewer {
/**
* This class is used to improve source editing performance by coalescing
* multiple notifications for an element change into a single refresh
*/
class DelayedRefreshTimer implements Runnable {
private final int delta = 2000;
protected Object objectPendingRefresh;
protected ISelection pendingSelection;
protected Object prevObject;
protected XMLTableTreeViewer viewer;
public DelayedRefreshTimer(XMLTableTreeViewer treeViewer) {
this.viewer = treeViewer;
}
public boolean isRefreshPending() {
return objectPendingRefresh != null;
}
public void refresh(Object object) {
if (prevObject == object) {
objectPendingRefresh = object;
getDisplay().timerExec(delta, this);
} else {
if (objectPendingRefresh != null) {
viewer.doRefresh(objectPendingRefresh, false);
objectPendingRefresh = null;
}
viewer.doRefresh(object, false);
}
prevObject = object;
}
private Display getDisplay() {
return PlatformUI.getWorkbench().getDisplay();
}
public void run() {
// defect 239677 ensure that the viewer's control is not disposed
//
if (objectPendingRefresh != null && !viewer.getTree().isDisposed()) {
viewer.doRefresh(objectPendingRefresh, true);
if (pendingSelection != null) {
// see fireSelectionChanged comment about jumping cursor
// problem
//
viewer.setSelection(pendingSelection, true);
pendingSelection = null;
}
objectPendingRefresh = null;
prevObject = null;
}
}
public void setSelection(ISelection selection) {
pendingSelection = selection;
}
}
class DelayingNodeSelectionListener implements INodeSelectionListener {
public void nodeSelectionChanged(NodeSelectionChangedEvent event) {
// if (isNodeSelectionListenerEnabled &&
// !event.getSource().equals(this)) {
if (!event.getSource().equals(XMLTableTreeViewer.this)) {
List selectedNodes = event.getSelectedNodes();
ISelection selection = new StructuredSelection(selectedNodes);
// for performance purposes avoid large multi-selections
//
if (selectedNodes.size() < 100) {
if (timer.isRefreshPending()) {
timer.setSelection(selection);
} else {
setSelection(selection, true);
}
}
}
}
}
class InternalModelStateListener implements IModelStateListener {
public void modelAboutToBeChanged(IStructuredModel model) {
ignoreRefresh = true;
}
public void modelChanged(IStructuredModel model) {
ignoreRefresh = false;
refresh();
}
public void modelDirtyStateChanged(IStructuredModel model, boolean isDirty) {
}
public void modelResourceDeleted(IStructuredModel model) {
}
public void modelResourceMoved(IStructuredModel originalmodel, IStructuredModel movedmodel) {
}
}
class NodeActionMenuListener implements IMenuListener {
public void menuAboutToShow(IMenuManager menuManager) {
// used to disable NodeSelection listening while running
// NodeAction
XMLNodeActionManager nodeActionManager = new XMLNodeActionManager(fModel, XMLTableTreeViewer.this) {
public void beginNodeAction(NodeAction action) {
super.beginNodeAction(action);
}
public void endNodeAction(NodeAction action) {
super.endNodeAction(action);
}
};
nodeActionManager.fillContextMenu(menuManager, getSelection());
}
}
protected CellEditor cellEditor;
int count = 0;
protected IModelStateListener fInternalModelStateListener = new InternalModelStateListener();
protected IStructuredModel fModel = null;
protected INodeSelectionListener fNodeSelectionListener;
protected ViewerSelectionManager fViewerSelectionManager;
protected boolean ignoreRefresh;
protected DelayedRefreshTimer timer;
protected XMLTreeExtension treeExtension;
public XMLTableTreeViewer(Composite parent) {
super(parent, SWT.FULL_SELECTION | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
// set up providers
this.treeExtension = new XMLTreeExtension(getTree());
XMLTableTreeContentProvider provider = new XMLTableTreeContentProvider();
provider.addViewer(this);
setContentProvider(provider);
setLabelProvider(provider);
createContextMenu();
XMLDragAndDropManager.addDragAndDropSupport(this);
timer = new DelayedRefreshTimer(this);
}
/**
* This creates a context menu for the viewer and adds a listener as well
* registering the menu for extension.
*/
protected void createContextMenu() {
MenuManager contextMenu = new MenuManager("#PopUp"); //$NON-NLS-1$
contextMenu.add(new Separator("additions")); //$NON-NLS-1$
contextMenu.setRemoveAllWhenShown(true);
contextMenu.addMenuListener(new NodeActionMenuListener());
Menu menu = contextMenu.createContextMenu(getControl());
getControl().setMenu(menu);
}
protected void doRefresh(Object o, boolean fromDelayed) {
treeExtension.resetCachedData();
super.refresh(o);
}
protected void fireSelectionChanged(SelectionChangedEvent event) {
if (!getTree().isDisposed() && !getTree().isFocusControl()) {
// defect 246094
// Various jumping cursor problems are caused when a selection
// 'delayed' selection occurs.
// These delayed selections are caused two ways:
//
// - when DelayedRefreshTimer calls doRefresh() ... the
// 'preserveSelection' causes selection to occur
// - when DelayedRefreshTimer performs a 'pending' selection
//
// Since we only want to update the selectionManager on an explict
// user action
// (and not some selection that is merely a resonse to the
// selection manager)
// we ensure that the tree has focus control before firing events
// to the selectionManager.
//
removeSelectionChangedListener(fViewerSelectionManager);
super.fireSelectionChanged(event);
addSelectionChangedListener(fViewerSelectionManager);
} else {
super.fireSelectionChanged(event);
}
}
public INodeSelectionListener getNodeSelectionListener() {
if (fNodeSelectionListener == null)
fNodeSelectionListener = new DelayingNodeSelectionListener();
return fNodeSelectionListener;
}
public String getTitle() {
return XMLEditorResourceHandler.getResourceString("%XMLTableTreeViewer.0"); //$NON-NLS-1$
}
protected void handleDispose(DisposeEvent event) {
super.handleDispose(event);
treeExtension.dispose();
setModel(null);
setViewerSelectionManager(null);
// if (fViewerSelectionManager != null) {
// fViewerSelectionManager.removeNodeSelectionListener(getNodeSelectionListener());
// }
//
// fOverlayIconManager.setResource(null);
// super.handleDispose(event);
//
// if (fModel != null)
// fModel.removeModelStateListener(fInternalModelStateListener);
}
public void refresh() {
if (!ignoreRefresh && !getControl().isDisposed()) {
treeExtension.resetCachedData();
super.refresh();
// if (B2BHacks.IS_UNIX) {
// this is required to fix defect 193792
// this fixes the problem where the 'paintHandler'drawn portions
// of tree weren't repainted properly
//
getTree().redraw(0, 0, getTree().getBounds().width, getTree().getBounds().height, false);
getTree().update();
// }
}
}
public void refresh(Object o) {
if (!ignoreRefresh && !getControl().isDisposed() && timer != null) {
if (getTree().isVisible()) {
doRefresh(o, false);
} else {
timer.refresh(o);
}
}
}
public void setModel(IStructuredModel model) {
// remove
if (fModel != null) {
fModel.removeModelStateListener(fInternalModelStateListener);
}
fModel = model;
Document domDoc = null;
if (fModel != null && fModel instanceof XMLModel) {
model.addModelStateListener(fInternalModelStateListener);
ModelQuery mq = ModelQueryUtil.getModelQuery(model);
treeExtension.setModelQuery(mq);
domDoc = ((XMLModel) fModel).getDocument();
setInput(domDoc);
}
}
// the following methods implement the IDesignViewer interface
// - getControl() is implemented via AdvancedTableTreeViewer
//
public void setViewerSelectionManager(ViewerSelectionManager viewerSelectionManager) {
// disconnect from old one
if (fViewerSelectionManager != null) {
fViewerSelectionManager.removeNodeSelectionListener(getNodeSelectionListener());
removeSelectionChangedListener(fViewerSelectionManager);
}
fViewerSelectionManager = viewerSelectionManager;
// connect to new one
if (fViewerSelectionManager != null) {
fViewerSelectionManager.addNodeSelectionListener(getNodeSelectionListener());
addSelectionChangedListener(fViewerSelectionManager);
}
}
}