blob: 57e7620c77f35352f7ee5706e2c801168fa1372c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 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
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.sse.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Event;
import org.eclipse.wst.sse.core.IModelStateListener;
import org.eclipse.wst.sse.core.IStructuredModel;
import org.eclipse.wst.sse.core.IndexedRegion;
import org.eclipse.wst.sse.core.util.Debug;
import org.eclipse.wst.sse.core.util.Utilities;
import org.eclipse.wst.sse.ui.extensions.spellcheck.SpellCheckSelectionListener;
import org.eclipse.wst.sse.ui.extensions.spellcheck.SpellCheckSelectionManager;
import org.eclipse.wst.sse.ui.internal.Logger;
import org.eclipse.wst.sse.ui.view.events.CaretEvent;
import org.eclipse.wst.sse.ui.view.events.INodeSelectionListener;
import org.eclipse.wst.sse.ui.view.events.ITextSelectionListener;
import org.eclipse.wst.sse.ui.view.events.NodeSelectionChangedEvent;
import org.eclipse.wst.sse.ui.view.events.TextSelectionChangedEvent;
public class ViewerSelectionManagerImpl implements ViewerSelectionManager, SpellCheckSelectionManager {
class InternalModelStateListener implements IModelStateListener {
public void modelAboutToBeChanged(IStructuredModel model) {
setModelChanging(true);
}
public void modelChanged(IStructuredModel model) {
setModelChanging(false);
}
public void modelDirtyStateChanged(IStructuredModel model, boolean isDirty) {
}
public void modelResourceDeleted(IStructuredModel model) {
}
public void modelResourceMoved(IStructuredModel originalmodel, IStructuredModel movedmodel) {
}
}
private CaretMediator fCaretMeditator;
private int fCaretPosition = 0;
protected IStructuredModel fModel;
private boolean fModelChanging = false;
private IDoubleClickListener[] fNodeDoubleClickListeners;
private INodeSelectionListener[] fNodeSelectionListeners;
private List fSelectedNodes;
// ISpellCheckSelectionManager
protected SpellCheckSelectionListener[] fSpellCheckSelectionListeners;
private int fTextSelectionEnd;
// TODO: private field never read locally
IndexedRegion fTextSelectionEndNode;
private ITextSelectionListener[] fTextSelectionListeners;
private int fTextSelectionStart;
// TODO: private field never read locally
IndexedRegion fTextSelectionStartNode;
private ITextViewer fTextViewer;
private InternalModelStateListener internalModelStateListener;
// TODO: private field never read locally
boolean isFiringNodeDoubleClick = false;
private boolean isFiringNodeSelectionChanged = false;
public ViewerSelectionManagerImpl() {
internalModelStateListener = new InternalModelStateListener();
}
public ViewerSelectionManagerImpl(ITextViewer textViewer) {
setTextViewer(textViewer);
internalModelStateListener = new InternalModelStateListener();
}
public synchronized void addNodeDoubleClickListener(IDoubleClickListener listener) {
// since its most certainly an error if someone is trying to add a
// null listner,
// we'll flag it explicitly and ignore
if (listener == null) {
Logger.log(Logger.WARNING, "Likely error in ViewerSelectionManagerImpl::addNodeDoubleClickListener should not be called with null listener"); //$NON-NLS-1$
return;
}
// make sure listlistener not already in listening array
// (and if it is, print a warning to aid debugging, if needed)
if (Utilities.contains(fNodeDoubleClickListeners, listener)) {
if (Debug.displayWarnings) {
System.out.println("ViewerSelectionManager::addNodeDoubleClickListener. listener " + listener + " was added more than once. "); //$NON-NLS-2$//$NON-NLS-1$
}
} else {
if (Debug.debugStructuredDocument) {
System.out.println("ViewerSelectionManager::addNodeDoubleClickListener. Adding an instance of " + listener.getClass() + " as a listener on ViewerSelectionManager."); //$NON-NLS-2$//$NON-NLS-1$
}
int oldSize = 0;
if (fNodeDoubleClickListeners != null) {
// normally won't be null, but we need to be sure, for first
// time through
oldSize = fNodeDoubleClickListeners.length;
}
int newSize = oldSize + 1;
IDoubleClickListener[] newListeners = new IDoubleClickListener[newSize];
if (fNodeDoubleClickListeners != null) {
System.arraycopy(fNodeDoubleClickListeners, 0, newListeners, 0, oldSize);
}
// add listener to last position
newListeners[newSize - 1] = listener;
//
// now switch new for old
fNodeDoubleClickListeners = newListeners;
}
}
public synchronized void addNodeSelectionListener(INodeSelectionListener listener) {
// since its most certainly an error if someone is trying to add a
// null listner,
// we'll flag it explicitly and ignore
if (listener == null) {
Logger.log(Logger.WARNING, "Likely error in ViewerSelectionManagerImpl::addNodeSelectionListener should not but called with null listener"); //$NON-NLS-1$
return;
}
// make sure listener is not already in listening array
// (and if it is, print a warning to aid debugging, if needed)
if (Utilities.contains(fNodeSelectionListeners, listener)) {
if (Debug.displayWarnings) {
System.out.println("ViewerSelectionManager::addNodeSelectionListener. listener " + listener + " was added more than once. "); //$NON-NLS-2$//$NON-NLS-1$
}
} else {
if (Debug.debugStructuredDocument) {
System.out.println("ViewerSelectionManager::addNodeSelectionListener. Adding an instance of " + listener.getClass() + " as a listener on ViewerSelectionManager."); //$NON-NLS-2$//$NON-NLS-1$
}
int oldSize = 0;
if (fNodeSelectionListeners != null) {
// normally won't be null, but we need to be sure, for first
// time through
oldSize = fNodeSelectionListeners.length;
}
int newSize = oldSize + 1;
INodeSelectionListener[] newListeners = new INodeSelectionListener[newSize];
if (fNodeSelectionListeners != null) {
System.arraycopy(fNodeSelectionListeners, 0, newListeners, 0, oldSize);
}
// add listener to last position
newListeners[newSize - 1] = listener;
//
// now switch new for old
fNodeSelectionListeners = newListeners;
}
}
/**
* @see SpellCheckSelectionManager#addSpellCheckSelectionListener(SpellCheckSelectionListener)
*/
public void addSpellCheckSelectionListener(SpellCheckSelectionListener listener) {
// since its most certainly an error if someone is trying to add a
// null listner,
// we'll flag it explicitly and ignore
if (listener == null) {
Logger.log(Logger.WARNING, "Likely error in ViewerSelectionManagerImpl::addSpellCheckSelectionListener should not but called with null listener"); //$NON-NLS-1$
return;
}
// make sure listener is not already in listening array
// (and if it is, print a warning to aid debugging, if needed)
if (Utilities.contains(fSpellCheckSelectionListeners, listener)) {
if (Debug.displayWarnings) {
System.out.println("ViewerSelectionManager::addSpellCheckSelectionListener. listener " + listener + " was added more than once. "); //$NON-NLS-2$//$NON-NLS-1$
}
} else {
if (Debug.debugStructuredDocument) {
System.out.println("ViewerSelectionManager::addSpellCheckSelectionListener. Adding an instance of " + listener.getClass() + " as a listener on ViewerSelectionManager."); //$NON-NLS-2$//$NON-NLS-1$
}
int oldSize = 0;
if (fSpellCheckSelectionListeners != null) {
// normally won't be null, but we need to be sure, for first
// time through
oldSize = fSpellCheckSelectionListeners.length;
}
int newSize = oldSize + 1;
SpellCheckSelectionListener[] newListeners = new SpellCheckSelectionListener[newSize];
if (fSpellCheckSelectionListeners != null) {
System.arraycopy(fSpellCheckSelectionListeners, 0, newListeners, 0, oldSize);
}
// add listener to last position
newListeners[newSize - 1] = listener;
//
// now switch new for old
fSpellCheckSelectionListeners = newListeners;
}
}
public synchronized void addTextSelectionListener(ITextSelectionListener listener) {
// since its most certainly an error if someone is trying to add a
// null listner,
// we'll flag it explicitly and ignore
if (listener == null) {
Logger.log(Logger.WARNING, "Likely error in ViewerSelectionManagerImpl::addTextSelectionListener should not but called with null listener"); //$NON-NLS-1$
return;
}
// make sure listener is not already in listening array
// (and if it is, print a warning to aid debugging, if needed)
if (Utilities.contains(fTextSelectionListeners, listener)) {
if (Debug.displayWarnings) {
System.out.println("ViewerSelectionManager::addTextSelectionListener. listener " + listener + " was added more than once. "); //$NON-NLS-2$//$NON-NLS-1$
}
} else {
if (Debug.debugStructuredDocument) {
System.out.println("ViewerSelectionManager::addTextSelectionListener. Adding an instance of " + listener.getClass() + " as a listener on ViewerSelectionManager."); //$NON-NLS-2$//$NON-NLS-1$
}
int oldSize = 0;
if (fTextSelectionListeners != null) {
// normally won't be null, but we need to be sure, for first
// time through
oldSize = fTextSelectionListeners.length;
}
int newSize = oldSize + 1;
ITextSelectionListener[] newListeners = new ITextSelectionListener[newSize];
if (fTextSelectionListeners != null) {
System.arraycopy(fTextSelectionListeners, 0, newListeners, 0, oldSize);
}
// add listener to last position
newListeners[newSize - 1] = listener;
//
// now switch new for old
fTextSelectionListeners = newListeners;
}
}
/**
* This method listens to text widget caret movements. This method is
* called when a caret (insertion point) move occurs that is NOT the
* result of the text changing. Specifcally, as a result of mouse clicks,
* PAGE_UP, RIGHT_ARROW, etc.
*/
public void caretMoved(final CaretEvent event) {
if (!isModelChanging()) {
List selectedNodes = getTextWidgetSelectedNodes(event.getPosition());
int caretPosition = event.getPosition();
processSelectionChanged(event.getSource(), selectedNodes, caretPosition, caretPosition);
}
}
protected void currentNodeChanged(Object source, List newSelectedNodes, int caretPosition) {
// save current node; make sure to clone the selected nodes list, or
// else there may be a side effect when listeners modify the list
fSelectedNodes = new Vector(newSelectedNodes);
// save current caret position
fCaretPosition = caretPosition;
// generate and fire event
NodeSelectionChangedEvent nodeSelectionChangedEvent = new NodeSelectionChangedEvent(source, newSelectedNodes, caretPosition);
fireNodeSelectionChangedEvent(nodeSelectionChangedEvent);
}
/**
* Notifies of a double click.
*
* @param event
* event object describing the double-click
*/
public void doubleClick(DoubleClickEvent event) {
if (!isModelChanging())
fireNodeDoubleClickEvent(event);
}
protected void fireNodeDoubleClickEvent(DoubleClickEvent event) {
if ((fNodeDoubleClickListeners != null) && (!isModelChanging())) {
// we must assign listeners to local variable to be thread safe,
// since the add and remove listner methods
// can change this object's actual instance of the listener array
// from another thread
// (and since object assignment is atomic, we don't need to
// synchronize
isFiringNodeDoubleClick = true;
try {
IDoubleClickListener[] holdListeners = fNodeDoubleClickListeners;
for (int i = 0; i < holdListeners.length; i++) {
holdListeners[i].doubleClick(event);
}
} finally {
isFiringNodeDoubleClick = false;
}
}
}
protected void fireNodeSelectionChangedEvent(NodeSelectionChangedEvent event) {
if ((fNodeSelectionListeners != null) && (!isModelChanging())) {
// we must assign listeners to local variable to be thread safe,
// since the add and remove listner methods
// can change this object's actual instance of the listener array
// from another thread
// (and since object assignment is atomic, we don't need to
// synchronize
isFiringNodeSelectionChanged = true;
try {
INodeSelectionListener[] holdListeners = fNodeSelectionListeners;
for (int i = 0; i < holdListeners.length; i++) {
holdListeners[i].nodeSelectionChanged(event);
}
} finally {
isFiringNodeSelectionChanged = false;
}
}
}
protected void fireSpellCheckSelectionChangedEvent() {
if ((fSpellCheckSelectionListeners != null) && (!isModelChanging())) {
// we must assign listeners to local variable to be thread safe,
// since the add and remove listner methods
// can change this object's actual instance of the listener array
// from another thread
// (and since object assignment is atomic, we don't need to
// synchronize
SpellCheckSelectionListener[] holdListeners = fSpellCheckSelectionListeners;
//
for (int i = 0; i < holdListeners.length; i++) {
holdListeners[i].selectionChanged();
}
}
}
protected void fireTextSelectionChangedEvent(TextSelectionChangedEvent event) {
if ((fTextSelectionListeners != null) && (!isModelChanging())) {
// we must assign listeners to local variable to be thread safe,
// since the add and remove listner methods
// can change this object's actual instance of the listener array
// from another thread
// (and since object assignment is atomic, we don't need to
// synchronize
ITextSelectionListener[] holdListeners = fTextSelectionListeners;
//
for (int i = 0; i < holdListeners.length; i++) {
holdListeners[i].textSelectionChanged(event);
}
}
}
public int getCaretPosition() {
return fCaretPosition;
}
public List getSelectedNodes() {
return fSelectedNodes;
}
public ITextViewer getTextViewer() {
return fTextViewer;
}
protected List getTextWidgetSelectedNodes(int offset) {
if (fModel == null)
return new ArrayList(0);
IndexedRegion firstSelectedNode = fModel.getIndexedRegion(offset);
fTextSelectionStartNode = firstSelectedNode;
fTextSelectionEndNode = firstSelectedNode;
// Never send a "null" in the selection
List selectedNodes = null;
if (firstSelectedNode != null) {
selectedNodes = new ArrayList(1);
selectedNodes.add(firstSelectedNode);
} else {
selectedNodes = new ArrayList(0);
}
return selectedNodes;
}
protected boolean isCurrentNodeChanged(List newSelectedNodes) {
return !newSelectedNodes.equals(fSelectedNodes);
}
protected boolean isModelChanging() {
return fModelChanging;
}
protected boolean isTextSelectionChanged(int textSelectionStart, int textSelectionEnd) {
return ((fTextSelectionStart != textSelectionStart) || (fTextSelectionEnd != textSelectionEnd));
}
protected void processSelectionChanged(Object source, List selectedNodes, int selectionStart, int selectionEnd) {
if (source == null) {
// source should not be null.
// log this and ignore this selection changed event.
// DMW: I "shortened" this log message by removing stack trace,
// after receiving log
// from support showing this error. It made the log file
// confusing.
// Not clear why the source was null, but probably only related to
// initialization, or 'startup' with file open.
Logger.log(Logger.ERROR, "ViewerSelectionManager::processSelectionChanged. Unexpected null source"); //$NON-NLS-1$
} else {
if (isTextSelectionChanged(selectionStart, selectionEnd))
textSelectionChanged(source, selectionStart, selectionEnd);
if (isCurrentNodeChanged(selectedNodes))
currentNodeChanged(source, selectedNodes, selectionEnd);
}
}
public void release() {
setTextViewer(null);
// remove this viewer selection manager from the old model's list of
// model state listeners
if (fModel != null)
fModel.removeModelStateListener(internalModelStateListener);
// make sure the CaretMediator we created is released as well
fCaretMeditator.release();
}
public synchronized void removeNodeDoubleClickListener(IDoubleClickListener listener) {
if ((fNodeDoubleClickListeners != null) && (listener != null)) {
// if its not in the listeners, we'll ignore the request
if (Utilities.contains(fNodeDoubleClickListeners, listener)) {
int oldSize = fNodeDoubleClickListeners.length;
int newSize = oldSize - 1;
IDoubleClickListener[] newListeners = new IDoubleClickListener[newSize];
int index = 0;
for (int i = 0; i < oldSize; i++) {
if (fNodeDoubleClickListeners[i] == listener) { // ignore
} else {
// copy old to new if its not the one we are removing
newListeners[index++] = fNodeDoubleClickListeners[i];
}
}
// now that we have a new array, let's switch it for the old
// one
fNodeDoubleClickListeners = newListeners;
}
}
}
public synchronized void removeNodeSelectionListener(INodeSelectionListener listener) {
if ((fNodeSelectionListeners != null) && (listener != null)) {
// if its not in the listeners, we'll ignore the request
if (Utilities.contains(fNodeSelectionListeners, listener)) {
int oldSize = fNodeSelectionListeners.length;
int newSize = oldSize - 1;
INodeSelectionListener[] newListeners = new INodeSelectionListener[newSize];
int index = 0;
for (int i = 0; i < oldSize; i++) {
if (fNodeSelectionListeners[i] == listener) { // ignore
} else {
// copy old to new if its not the one we are removing
newListeners[index++] = fNodeSelectionListeners[i];
}
}
// now that we have a new array, let's switch it for the old
// one
fNodeSelectionListeners = newListeners;
}
}
}
/**
* @see SpellCheckSelectionManager#removeSpellCheckSelectionListener(SpellCheckSelectionListener)
*/
public void removeSpellCheckSelectionListener(SpellCheckSelectionListener listener) {
if ((fSpellCheckSelectionListeners != null) && (listener != null)) {
// if its not in the listeners, we'll ignore the request
if (Utilities.contains(fSpellCheckSelectionListeners, listener)) {
int oldSize = fSpellCheckSelectionListeners.length;
int newSize = oldSize - 1;
SpellCheckSelectionListener[] newListeners = new SpellCheckSelectionListener[newSize];
int index = 0;
for (int i = 0; i < oldSize; i++) {
if (fSpellCheckSelectionListeners[i] == listener) { // ignore
} else {
// copy old to new if its not the one we are removing
newListeners[index++] = fSpellCheckSelectionListeners[i];
}
}
// now that we have a new array, let's switch it for the old
// one
fSpellCheckSelectionListeners = newListeners;
}
}
}
public synchronized void removeTextSelectionListener(ITextSelectionListener listener) {
if ((fTextSelectionListeners != null) && (listener != null)) {
// if its not in the listeners, we'll ignore the request
if (Utilities.contains(fTextSelectionListeners, listener)) {
int oldSize = fTextSelectionListeners.length;
int newSize = oldSize - 1;
ITextSelectionListener[] newListeners = new ITextSelectionListener[newSize];
int index = 0;
for (int i = 0; i < oldSize; i++) {
if (fTextSelectionListeners[i] == listener) { // ignore
} else {
// copy old to new if its not the one we are removing
newListeners[index++] = fTextSelectionListeners[i];
}
}
// now that we have a new array, let's switch it for the old
// one
fTextSelectionListeners = newListeners;
}
}
}
public void selectionChanged(SelectionChangedEvent event) {
// If selection is fired as a result of processing a node change
// event, then ignore the selection.
if (isFiringNodeSelectionChanged || isModelChanging())
return;
ISelection eventSelection = event.getSelection();
// handle Structured selections
if (eventSelection instanceof IStructuredSelection) {
IStructuredSelection selection = (IStructuredSelection) eventSelection;
// System.out.println("selection: " + event.getSource() + " [" +
// selection.toArray().length + "] " +
// selection.getFirstElement());
List selectedNodes = selection.toList();
int selectionStart = 0;
int selectionEnd = 0;
// something selected
if (selectedNodes.size() > 0) {
IndexedRegion firstSelectedNode = (IndexedRegion) selectedNodes.get(0);
selectionStart = firstSelectedNode.getStartOffset();
selectionEnd = firstSelectedNode.getEndOffset();
// remove all except the first selected node
selectedNodes = new Vector(1);
selectedNodes.add(firstSelectedNode);
}
processSelectionChanged(event.getSource(), selectedNodes, selectionStart, selectionEnd);
}
// handle text selection changes
else if (eventSelection instanceof ITextSelection) {
ITextSelection selection = (ITextSelection) eventSelection;
// System.out.println("selection: " + event.getSource() + " (" +
// selection.getOffset() + "+=" + selection.getLength() + ")");
int selectionStart = selection.getOffset();
setCaretPosition(selectionStart);
int selectionEnd = selection.getOffset() + selection.getLength();
if (true) {
// option 1: works great for Source Page editors and the XML
// and XSL Editors
List selectedNodes = getTextWidgetSelectedNodes(selection.getOffset());
processSelectionChanged(event.getSource(), selectedNodes, selectionStart, selectionEnd);
} else {
// option 2: works with all of the above plus Page Designer,
// but not as clean nor perfectly
// TODO: switch to option 1
Event selectionEvent = new Event();
selectionEvent.widget = fTextViewer.getTextWidget();
selectionEvent.display = fTextViewer.getTextWidget().getDisplay();
selectionEvent.x = selectionStart;
selectionEvent.y = selectionEnd;
fTextViewer.getTextWidget().setSelection(selectionStart, selectionEnd);
widgetDefaultSelected(new SelectionEvent(selectionEvent));
}
}
}
protected void setCaretPosition(int caretPosition) {
fCaretPosition = caretPosition;
}
public void setModel(IStructuredModel newModel) {
if (newModel != fModel) {
// remove this viewer selection manager from the old model's list
// of model state listeners
if (fModel != null)
fModel.removeModelStateListener(internalModelStateListener);
fModel = newModel;
// add this viewer selection manager to the new model's list of
// model state listeners
fModel.addModelStateListener(internalModelStateListener);
}
}
protected void setModelChanging(boolean modelChanging) {
fModelChanging = modelChanging;
}
/**
* This is the viewer who's caret postion we monitor to determine when to
* check if the node has changed. We don't actually need, or save, the
* viewer, but do need its text widget, and will register this viewer
* selection manager as a listener of the text widget's text selection.
*/
public void setTextViewer(ITextViewer newTextViewer) {
// unhook from previous, if any
if (fCaretMeditator != null) {
fCaretMeditator.removeCaretListener(this);
fCaretMeditator.setTextWidget(null);
}
if (fTextViewer != null) {
// fTextViewer.getSelectionProvider().removeSelectionChangedListener(this);
StyledText textWidget = fTextViewer.getTextWidget();
if (textWidget != null) {
// listen to text selections
textWidget.removeSelectionListener(this);
}
}
fTextViewer = newTextViewer;
if (fTextViewer != null) {
// fTextViewer.getSelectionProvider().addSelectionChangedListener(this);
StyledText textWidget = fTextViewer.getTextWidget();
if (textWidget != null) {
// create new caret mediator, if it doesn't exist yet
if (fCaretMeditator == null) {
fCaretMeditator = new CaretMediator(textWidget);
} else {
fCaretMeditator.setTextWidget(textWidget);
}
// and register as a listener
fCaretMeditator.addCaretListener(this);
// listen to text selections
textWidget.addSelectionListener(this);
}
}
}
protected void textSelectionChanged(Object source, int textSelectionStart, int textSelectionEnd) {
// save current text selection
fTextSelectionStart = textSelectionStart;
fTextSelectionEnd = textSelectionEnd;
// save current caret position
fCaretPosition = fTextSelectionEnd;
// generate and fire event
TextSelectionChangedEvent textSelectionChangedEvent = new TextSelectionChangedEvent(source, fTextSelectionStart, fTextSelectionEnd);
fireTextSelectionChangedEvent(textSelectionChangedEvent);
// SpellCheck dialog also needs to listen text selection change
fireSpellCheckSelectionChangedEvent();
}
/**
* This method listens to text widget default selection changes. This
* method is called when default selection occurs in the control. For
* example, on some platforms default selection occurs in a List when the
* user double-clicks an item or types return in a Text.
*/
public void widgetDefaultSelected(SelectionEvent event) {
if (!isModelChanging()) {
List selectedNodes = getTextWidgetSelectedNodes(event.x);
int selectionStart = event.x;
int selectionEnd = event.y;
processSelectionChanged(event.getSource(), selectedNodes, selectionStart, selectionEnd);
}
}
/**
* This method listens to text widget text selection changes. This method
* is called when the text selection in a text widget has changed.
*/
public void widgetSelected(SelectionEvent event) {
// If selection is fired as a result of processing a node change
// event, then ignore the selection.
if (isFiringNodeSelectionChanged || isModelChanging())
return;
widgetDefaultSelected(event);
}
}