/* * � Copyright IBM Corp. 2011 * * 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.ibm.xsp.extlib.designer.tooling.panels; import static com.ibm.xsp.extlib.designer.tooling.constants.IExtLibAttrNames.EXT_LIB_ATTR_CONFIGURATION; import static com.ibm.xsp.extlib.designer.tooling.constants.IExtLibAttrNames.EXT_LIB_ATTR_LABEL; import static com.ibm.xsp.extlib.designer.tooling.constants.IExtLibRegistry.EXT_LIB_NAMESPACE_URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Stack; import org.eclipse.gef.commands.CommandStack; import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; 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.custom.ScrolledComposite; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.ibm.commons.iloader.node.DataNode; import com.ibm.commons.iloader.node.DataNodeAdapter; import com.ibm.commons.iloader.node.DataNodeListener; import com.ibm.commons.iloader.node.IClassDef; import com.ibm.commons.iloader.node.ICollection; import com.ibm.commons.iloader.node.ILoader; import com.ibm.commons.iloader.node.IMember; import com.ibm.commons.iloader.node.IObjectCollection; import com.ibm.commons.iloader.node.NodeException; import com.ibm.commons.iloader.node.collections.SingleCollection; import com.ibm.commons.swt.SWTLayoutUtils; import com.ibm.commons.swt.SWTUtils; import com.ibm.commons.swt.controls.custom.CustomButton; import com.ibm.commons.swt.controls.custom.CustomComposite; import com.ibm.commons.swt.controls.custom.CustomTree; import com.ibm.commons.swt.controls.custom.CustomTreeColumn; import com.ibm.commons.swt.data.controls.DCUtils; import com.ibm.commons.swt.data.layouts.PropLayout2; import com.ibm.commons.util.StringUtil; import com.ibm.designer.domino.xsp.api.panels.IPanelDataReciever; import com.ibm.designer.domino.xsp.api.panels.IPanelExtraData; import com.ibm.designer.domino.xsp.api.panels.complex.ComplexPanelComposite; import com.ibm.designer.domino.xsp.api.util.XPagesDOMUtil; import com.ibm.designer.domino.xsp.api.util.XPagesEditorUtils; import com.ibm.designer.domino.xsp.api.util.XPagesPropertiesViewUtils; import com.ibm.designer.domino.xsp.registry.DesignerExtension; import com.ibm.designer.domino.xsp.registry.DesignerExtensionUtil; import com.ibm.xsp.extlib.designer.tooling.ExtLibToolingPlugin; import com.ibm.xsp.extlib.designer.tooling.commands.RemoveNodeCommand; import com.ibm.xsp.extlib.designer.tooling.constants.IExtLibAttrNames; import com.ibm.xsp.extlib.designer.tooling.constants.IExtLibTagNames; import com.ibm.xsp.extlib.designer.tooling.panels.actions.RedoAction; import com.ibm.xsp.extlib.designer.tooling.panels.actions.UndoAction; import com.ibm.xsp.extlib.designer.tooling.panels.model.LinkContentProvider; import com.ibm.xsp.extlib.designer.tooling.panels.model.NodeLabelProvider; import com.ibm.xsp.extlib.designer.tooling.utils.ExtLibRegistryUtil; import com.ibm.xsp.extlib.designer.tooling.utils.ExtLibToolingLogger; import com.ibm.xsp.extlib.designer.tooling.utils.XSPNodeUtil; import com.ibm.xsp.extlib.tree.ITreeNode; import com.ibm.xsp.registry.FacesContainerProperty; import com.ibm.xsp.registry.FacesDefinition; import com.ibm.xsp.registry.FacesProperty; import com.ibm.xsp.registry.FacesRegistry; /** * This abstract panel encapsulates all of the functionality needed to create a "Links" panel for the * applicationLayout tag. The panel has two main sections, the left column and right column. * The left column will display a TreeViewer. The TreeViewer will display the link node children * of the attribute provided via the abstract method {@link #getLinkAttributeName()}.<br/> * When items are selected in the Tree the right column will be updated. This works similar * to the Data Panel on an XPage. An item is selected in the TreeViewer and as a result a corresponding * properties panel is displayed in the right hand column. * * * @author doconnor * */ public abstract class AbstractTreeNodePanel extends PropLayout2 implements IPanelDataReciever { final private TreeNodePanelDescriptor _descriptor; private Menu _childLinkMenu = null; //the menu that will show "Add Child Link" items private ComplexPanelComposite _complexComposite = null; //the composite that will house the UI for the selected node private IPanelExtraData _data = null; //data access to the xpage private Composite _leftChild = null; //the left composite - needed so we can retrieve the DataNode as needed private Menu _linkMenu = null; //the menu that will show "Add Link" items private TreeViewer _linkViewer = null; //the viewer that will show all of the links for the current tag private CustomButton _addLink = null; // the button for adding link tree nodes to tree nodes private CustomButton _addChildLink = null; // the button for adding link tree nodes to tree nodes that can have children private CustomButton _removeLink = null; // the button for deleting link tree nodes private CustomButton _moveUp = null; private CustomButton _moveDown = null; private DataNodeListener dataNodeListener = new DataNodeAdapter() { public void onValueChanged2( DataNode source, int record, Object object, IMember member ) { AbstractTreeNodePanel.this._linkViewer.refresh(); } }; private KeyListener delKeyListener = new KeyAdapter() { public void keyPressed(KeyEvent event) { switch(event.keyCode) { case SWT.DEL: { AbstractTreeNodePanel.this.removeSelected(); break; } }; } }; protected abstract String getLinkAttributeDescription(); /** * Constructs a new instance of this class * @param parent * @param style */ public AbstractTreeNodePanel(Composite parent, TreeNodePanelDescriptor descriptor, int style) { super(parent, style); _descriptor = descriptor; } /** * Creates a link menu item. Adds a listener to the item, the listener * adds a child node to the applicationLayout. If <code>createChild</code> is * true then a child will be added to the currently selected node. * * @param menu * the parent menu item * @param label * the label of the menu item * @param def * the FacesDefinition of the TreeNode represented by the menu item * @param createChild * a flag that determines whether or not the TreeNode being inserted should be inserted as a * child of another TreeNode or as a child of the applicationLayout tag. */ protected void addLinkMenuItem(Menu menu, final String label, final FacesDefinition def, final boolean createChild) { MenuItem item = new MenuItem(menu, SWT.PUSH); item.setText(label); item.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { Object newObject = null; Object parentObj = null; DataNode dn = DCUtils.findDataNode(_leftChild, true); IMember member = dn.getMember(_descriptor.propertyName); ILoader loader = dn.getLoader(); IClassDef classDef; try { classDef = loader.loadClass(def.getNamespaceUri(), def.getTagName()); parentObj = dn.getCurrentObject(); newObject = classDef.newInstance(parentObj); // if we're creating a child node, get the parent from the tree selection if(createChild){ // this DN is set when selection changes in the tree DataNode dnParent = DCUtils.findDataNode(_complexComposite, true); // find the first member of type "ITreeNode" IMember parentMember = AbstractTreeNodePanel.this.findTreeNodeMember(dnParent.getCurrentObject(), loader, null); // and change whose collection we'll add to if (parentMember instanceof ICollection) { parentObj = dnParent.getCurrentObject(); member = parentMember; } } IObjectCollection col = loader.getObjectCollection(parentObj, (ICollection) member); col.add(col.size(), newObject, null); // add a default label if appropriate if(def.getProperty(EXT_LIB_ATTR_LABEL) != null && newObject instanceof Element){ //TODO DAN - Should use data node here.. should avoid instanceof Element if possible. if(def.getProperty(IExtLibAttrNames.EXT_LIB_ATTR_VAR) != null){ String var = getLinkDisplayName(def, false); if(StringUtil.isEmpty(var)){ var = "var"; // $NON-NLS-1$ } else{ char[] cs = var.toCharArray(); if(cs != null){ StringBuffer buff = new StringBuffer(); for(char c : cs){ if(!Character.isSpaceChar(c)){ buff.append(c); } } var = buff.toString(); } } String[] vars = XPagesDOMUtil.getVars(((Element)newObject).getOwnerDocument(), null); var = XPagesDOMUtil.generateUniqueVar(Arrays.asList(vars), (Element)newObject, var); XPagesDOMUtil.setAttribute((Element)newObject, IExtLibAttrNames.EXT_LIB_ATTR_VAR, var); } String newLabel = generateNewLabel(def); XPagesDOMUtil.setAttribute((Element)newObject, EXT_LIB_ATTR_LABEL, newLabel); } } catch (NodeException e) { if (ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.isErrorEnabled()) { ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.errorp(this, "addLinkMenuItem", e, "Failed to create new {0}", // $NON-NLS-1$ $NLE-AbstractTreeNodePanel.Failedtocreatenew0-2$ _descriptor.propertyName); } } if(newObject != null){ _linkViewer.refresh(); if (null != parentObj){ _linkViewer.setExpandedState(parentObj, true); } _linkViewer.setSelection(new StructuredSelection(newObject)); } } }); } /** * @param treenodeTypeName * @return the names of any propertoes of 'treenodeTypeName' that are required. */ protected List<String> checkRequired(String treenodeTypeName) { List<String> propnames = new ArrayList<String>(); FacesRegistry registry = _data.getDesignerProject().getFacesRegistry(); FacesDefinition propDef = registry.findDef(EXT_LIB_NAMESPACE_URI, treenodeTypeName); for (String propname : propDef.getDefinedPropertyNames()) { boolean required = ExtLibRegistryUtil.getIsRequiredProperty(registry, propDef.getId(), propname); if (required) { propnames.add(propname); } } return propnames; } /** * Creates a new menu and adds a menu item for each TreeNode returned by {@link #findTreeNodes()}. * * @param parent * the shell parent of the menu * @param createChild * a flag which specifies whether the menu item should deal with children of a TreeNode or add * root TreeNodes * * @return * the menu containing menu items returned by {@link #findTreeNodes()} */ private Menu addLinkTypes(Shell parent, boolean createChild) { Menu menu = new Menu(parent); SWTUtils.setControlId(menu, "menu.links.add"); //$NON-NLS-1$ List<FacesDefinition> treeNodes = findTreeNodes(); if (treeNodes != null) { for (final FacesDefinition def : treeNodes) { // @todo: do we want to look for required properties here? String label = getLinkDisplayName(def, true); addLinkMenuItem(menu, label, def, createChild); } } return menu; } /** * * @param def * @return */ protected String getLinkDisplayName(FacesDefinition def, boolean forMenu) { if(!forMenu && StringUtil.equals(def.getTagName(), IExtLibTagNames.EXT_LIB_TAG_BASIC_LEAF_NODE)){ return "Link"; // $NLX-AbstractTreeNodePanel.Link-1$ } DesignerExtension ext = DesignerExtensionUtil.getExtension(def); if (ext != null) { return ext.getDisplayName(); } return def.getTagName(); } /** * setup the DataNode for the panel using the member (attribute) name provided by {@link #getLinksContainingTypeName()} * @param leftChild * @param listener */ protected void setupLinkPanelDataNode(Composite leftChild, DataNodeListener listener) { ExtLibPanelUtil.initDataNode(leftChild, listener, _descriptor.complexName); } protected void removeLinkPanelDataNodeListener(DataNodeListener listener) { if(_leftChild != null){ DataNode dataNode = DCUtils.findDataNode(_leftChild, false); if (dataNode != null) { dataNode.removeDataNodeListener(AbstractTreeNodePanel.this.dataNodeListener); AbstractTreeNodePanel.this.dataNodeListener = null; } } } /** * create new unique text for a "label" attribute, if present, by looking at all existing. * @param displayName * @return */ protected String generateNewLabel(FacesDefinition def) { if(StringUtil.equals(def.getTagName(), IExtLibTagNames.EXT_LIB_TAG_DOMINO_VIEW_LIST)){ return null; //for the dominoViewListTreeNode tag we do not want to set the label! } String label = "Label"; // $NLX-AbstractTreeNodePanel.Label-1$ String displayName = getLinkDisplayName(def, false); if (null == displayName) return label; Object input = _linkViewer.getInput(); ITreeContentProvider cp = (ITreeContentProvider)_linkViewer.getContentProvider(); Object[] children = cp.getElements(input); Stack<Object> stack = new Stack<Object>(); // for non-recursive tree traversal stack.push(input); Set<String> set = new HashSet<String>(); // collects current names while (!stack.isEmpty()) { if (null != children) { for (int i = 0; i < children.length; i++) { if (children[i] instanceof Element) { String l = XPagesDOMUtil.getAttribute((Element)children[i], EXT_LIB_ATTR_LABEL); if (StringUtil.isNotEmpty(l)) set.add(l); } stack.push(children[i]); } } children = cp.getChildren(stack.pop()); } while (!stack.isEmpty()); String prefix = displayName; String[] splits = displayName.split(" "); //$NON-NLS-1$ if (null != splits && splits.length > 0) { prefix = splits[0]; for (int i = 1; i < 100; i++) { String thistry = prefix + " " + String.valueOf(i); //$NON-NLS-1$ if (!set.contains(thistry)) { label = thistry; break; } } } return label; } @Override protected void createLeftContents(Composite leftChild) { _leftChild = leftChild; // set up the data node, add listener to refresh tree in case "children" is edited // using right tree. setupLinkPanelDataNode(leftChild, this.dataNodeListener); _linkMenu = addLinkTypes(leftChild.getShell(), false); _childLinkMenu = addLinkTypes(leftChild.getShell(), true); CustomTree tree = new CustomTree(leftChild, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER, "tree.id"); // $NON-NLS-1$ tree.setLinesVisible(true); tree.setHeaderVisible(true); tree.setCols(55); GridData treeData = SWTLayoutUtils.createGDFill(); treeData.heightHint = 200; treeData.horizontalIndent = getControlIndentAmt(); tree.setLayoutData(treeData); CustomTreeColumn links = new CustomTreeColumn(tree, SWT.NONE, "links.id"); // $NON-NLS-1$ links.setText("Links"); // $NLX-AbstractTreeNodePanel.Links-1$ links.setWidthUnit(CustomTreeColumn.UNIT_PERCENT); links.setColWidth(100); tree.addKeyListener(delKeyListener); _linkViewer = new TreeViewer(tree); NodeLabelProvider labelProvider = new NodeLabelProvider(_data.getDesignerProject().getFacesRegistry()); _linkViewer.setLabelProvider(labelProvider); LinkContentProvider contentPvdr = new LinkContentProvider(_descriptor.propertyName, _data.getDesignerProject().getFacesRegistry()); _linkViewer.setContentProvider(contentPvdr); _linkViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection sel = event.getSelection(); boolean enableRemove = false; boolean enableAddChild = false; if (sel instanceof IStructuredSelection) { IStructuredSelection structured = (IStructuredSelection) sel; Object element = structured.getFirstElement(); if (element instanceof Element) { Element e = (Element) element; refreshMoveButtons(e); DataNode dn = DCUtils.findDataNode(_complexComposite, true); ILoader loader = dn.getLoader(); if (null == loader) { loader = XPagesPropertiesViewUtils.getXPagesMultiDomLoader(getExtraData().getDesignerProject()); } if (loader != null) { dn.setDataProvider(new SingleCollection(e)); try { dn.setClassDef(loader.getClassOf(e)); } catch (NodeException ne) { if(ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.isErrorEnabled()){ ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.error(ne, "Failed to get class def of element ", e.getTagName()); // $NLE-AbstractTreeNodePanel.Failedtogetclassdefofelement-1$ } } // enable "remove" enableRemove = true; // enablement for "Add Child"... IMember mTreeNode = AbstractTreeNodePanel.this.findTreeNodeMember(e, dn.getLoader(), null); enableAddChild = mTreeNode != null; } else { ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.warnp(AbstractTreeNodePanel.class, "selectionChanged", "No loader for current data node"); //$NON-NLS-1$ // $NLW-AbstractTreeNodePanel.Noloaderforcurrentdatanode-2$ } // show the properties on the right _complexComposite.updatePanel(e.getNamespaceURI(), e.getLocalName()); // re-layout parents until we get to a scrolled composite getDisplay().asyncExec(new Runnable() { /* * (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { if (AbstractTreeNodePanel.this.isDisposed()) { return; } Composite parent = _complexComposite; Composite prevParent = parent; while (parent != null && !parent.isDisposed()) { try { if (parent instanceof ScrolledComposite) { ((ScrolledComposite) parent).setMinSize(prevParent.computeSize(SWT.DEFAULT, SWT.DEFAULT)); break; } parent.layout(); } catch (Throwable t) { if (ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.isWarnEnabled()) { ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.warnp(AbstractTreeNodePanel.this, "selectionChanged", t, //$NON-NLS-1$ "Error encountered when refreshing applicationLayout properties panel"); // $NLW-AbstractTreeNodePanel.Errorencounteredwhenrefreshingapp-1$ } } prevParent = parent; parent = parent.getParent(); } } }); } else { // (it's not instanceof Element, must be null if (null == element && null != _complexComposite) { _complexComposite.updatePanel(null, null); } } } _removeLink.setEnabled(enableRemove); _addChildLink.setEnabled(enableAddChild); } }); _linkViewer.setInput(getDataNode()); _leftChild.addDisposeListener(new DisposeListener() { /* * (non-Javadoc) * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) */ public void widgetDisposed(DisposeEvent event) { removeLinkPanelDataNodeListener(dataNodeListener); } }); Composite buttonParent = new Composite(leftChild, SWT.NONE); buttonParent.setLayout(SWTLayoutUtils.createLayoutNoMarginDefaultSpacing(1)); GridData bpGD = SWTLayoutUtils.createGDFillVerticalNoGrab(); bpGD.verticalAlignment = GridData.BEGINNING; buttonParent.setLayoutData(bpGD); _moveUp = new CustomButton(buttonParent, SWT.NONE, "move.up.id");//$NON-NLS-1$ _moveUp.setToolTipText("Moves the currently selected item up"); // $NLX-AbstractTreeNodePanel.Movesthecurrentlyselecteditemup-1$ _moveUp.setImage(getDisabledImage(true)); _moveUp.addSelectionListener(new SelectionAdapter() { /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event) { super.widgetSelected(event); ISelection sel = _linkViewer.getSelection(); if(sel instanceof IStructuredSelection){ Object o = ((IStructuredSelection)sel).getFirstElement(); if(o instanceof Element){ moveUp((Element)o); } } } }); _moveDown = new CustomButton(buttonParent, SWT.NONE, "move.down.id");//$NON-NLS-1$ _moveDown.setToolTipText("Moves the currently selected item down"); // $NLX-AbstractTreeNodePanel.Movesthecurrentlyselecteditemdown-1$ _moveDown.setImage(getDisabledImage(false)); _moveDown.addSelectionListener(new SelectionAdapter() { /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent event) { super.widgetSelected(event); ISelection sel = _linkViewer.getSelection(); if(sel instanceof IStructuredSelection){ Object o = ((IStructuredSelection)sel).getFirstElement(); if(o instanceof Element){ moveDown((Element)o); } } } }); if(_linkViewer.getSelection() instanceof IStructuredSelection){ Object o = ((IStructuredSelection)_linkViewer.getSelection()).getFirstElement(); if(o instanceof Element){ refreshMoveButtons((Element)o); } } } @Override protected void createRightContents(Composite rightChild) { ((GridData)rightChild.getLayoutData()).horizontalAlignment = SWT.FILL; _complexComposite = new ComplexPanelComposite(rightChild, SWT.NONE); initComplexDataNode(_complexComposite); _complexComposite.updatePanelData(_data); _complexComposite.setLayout(SWTLayoutUtils.createLayoutNoMarginDefaultSpacing(1)); _complexComposite.setLayoutData(SWTLayoutUtils.createGDFill()); _complexComposite.setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); TreeItem[] items = _linkViewer.getTree().getItems(); if(items != null && items.length > 0){ Object firstItem = items[0].getData(); _linkViewer.setSelection(new StructuredSelection(firstItem)); } } private List<FacesDefinition> findTreeNodes() { if (this._data != null && this._data.getDesignerProject() != null) { FacesRegistry registry = this._data.getDesignerProject().getFacesRegistry(); if (registry != null) { return ExtLibRegistryUtil.getNodes(registry, _descriptor.complexType, _descriptor.propertyName); } } return null; } @Override protected int getNumLeftColumns() { return 2; } @Override protected int getNumRightColumns() { return 1; } private void initComplexDataNode(ComplexPanelComposite parent) { DataNode dataNode = DCUtils.findDataNode(parent, false); if (dataNode != null) { DCUtils.initDataBinding(parent.getParent()); DataNode newNode = DCUtils.findDataNode(parent, true); newNode.setClassDef(dataNode.getClassDef()); } } /** * */ private void removeSelected() { // save the current table selection Tree tree = _linkViewer.getTree(); TreeItem[] selectedItems = tree.getSelection(); int selectedIndex = -1; if (null != selectedItems && selectedItems.length > 0) { TreeItem[] items = tree.getItems(); if (null != items && items.length > 0) { for (int i = 0; i < items.length; i++) { if (selectedItems[0].equals(items[i])) { selectedIndex = i; break; } } if (selectedIndex == items.length -1) selectedIndex--; } } else{ //play the prompt sound! Display.getDefault().beep(); return; } // the DN for the complextComposite will be the current selection DataNode dnSelected = DCUtils.findDataNode(_complexComposite, true); ILoader loader = dnSelected.getLoader(); Element selected = (Element)dnSelected.getCurrentObject(); if (null == selected) { ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.warn("\"Delete\" links menu item pressed with no selected element"); //$NON-NLS-1$ return; } boolean doIt = true; // need to check if we want to warn that they are deleting a container with child elements NodeList childrenNodes = selected.getChildNodes(); if (null != childrenNodes && childrenNodes.getLength() > 0) { // does it have any non-text children? boolean hasChildElement = false; for (int i = 0; i < childrenNodes.getLength() && !hasChildElement; i++) { Node node = childrenNodes.item(i); hasChildElement = (node.getNodeType() == Node.ELEMENT_NODE); } // GGRD8UAP4U check if this -really- has children, or just child element nodes (like an empty <this.children>) if (hasChildElement) { ITreeContentProvider cp = (ITreeContentProvider)_linkViewer.getContentProvider(); Object[] childrenFromProvider = cp.getChildren(selected); if (childrenFromProvider == null || childrenFromProvider.length == 0) { hasChildElement = false; } } if (hasChildElement) { // can this have children? if so, get the member that contains the children IMember childrenMember = findTreeNodeMember(selected, loader, null); // if selected has child DOM Nodes and could have child tree nodes // (has a member of type iTreeNode), warn... if (childrenMember != null) { doIt = MessageDialog.openConfirm(getShell(), "Application Layout", // $NLX-AbstractTreeNodePanel.ApplicationLayout-1$ "This Node has children. Are you sure you want to remove it, and all of its children?"); // $NLX-AbstractTreeNodePanel.ThisNodehaschildrenAreyousureyouw-1$ } } } Node parent = selected.getParentNode(); if (doIt && null != parent) { String memberName = parent.getLocalName(); try { if (memberName.startsWith(IExtLibTagNames.TAG_THIS_PREFIX)){ memberName = memberName.substring(IExtLibTagNames.TAG_THIS_PREFIX.length()); } Node grandparentNode = selected.getParentNode().getParentNode(); IMember containerMember = findTreeNodeMember(grandparentNode, loader, memberName); if (containerMember instanceof ICollection) { IObjectCollection c = loader.getObjectCollection(grandparentNode, (ICollection)containerMember); if (null != c) { IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart(); IAction undo = null; IAction redo = null; CommandStack stack = null; IActionBars bars = null; if(part instanceof IViewPart){ bars = ((IViewPart)part).getViewSite().getActionBars(); if(bars != null){ undo = bars.getGlobalActionHandler(ActionFactory.UNDO.getId()); redo = bars.getGlobalActionHandler(ActionFactory.REDO.getId()); if(redo == null){ stack = XPagesEditorUtils.getCommandStack(null); redo = new RedoAction(stack); } if(undo == null){ if(stack == null){ stack = XPagesEditorUtils.getCommandStack(null); } undo = new UndoAction(stack, redo); if(redo instanceof RedoAction){ ((RedoAction)redo).setUndoAction((UndoAction)undo); } } bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), undo); bars.setGlobalActionHandler(ActionFactory.REDO.getId(), redo); } } for (int i=0; i < c.size(); i++){ // remove the member of the collection that is selected if (c.get(i).equals(selected)) { Node n = (Node)c.get(i); removeAndUpdateUndoActions(n, undo, redo, stack, bars); break; } } // GGRD8UAP4U get the children again.... if 0, remove the "this.children" IObjectCollection cpost = loader.getObjectCollection(grandparentNode, (ICollection)containerMember); if (null != cpost && cpost.size() == 0 && parent != null) { if (containerMember.getName().endsWith("children")) { //$NON-NLS-1$ removeAndUpdateUndoActions(parent, undo, redo, stack, bars); } } } } // try to select the previous (or next) one if (selectedIndex >= 0) { TreeItem[] items = tree.getItems(); if (selectedIndex < items.length) tree.select(items[selectedIndex]); } else { _complexComposite.updatePanel(null, null); } } catch(NodeException e) { if(ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.isErrorEnabled()){ ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.error(e, "Failed to get children for Data Node ", memberName); // $NLE-AbstractTreeNodePanel.FailedtogetchildrenforDataNode-1$ } } } } /* * */ private void removeAndUpdateUndoActions(Node n, IAction undo, IAction redo, CommandStack stack, IActionBars bars) { RemoveNodeCommand cmd = new RemoveNodeCommand(n); XPagesEditorUtils.executeCommand(cmd, (IEditorPart)null); if(stack != null){ if(undo != null){ undo.setEnabled(stack.canUndo()); } if(redo != null){ redo.setEnabled(stack.canRedo()); } if(bars != null){ bars.updateActionBars(); } } } private IMember findTreeNodeMember(Object element, ILoader loader, String nameToFind) { IMember member = null; if (null != element){ try { IClassDef classDef = loader.getClassOf(element); if (null != classDef) { Object obj = classDef.getNativeClass(); //@TODO: is getNativeClass() OK here? if (obj instanceof FacesDefinition) { FacesDefinition fdef = (FacesDefinition)obj; Collection<String> names = fdef.getPropertyNames(); for(String name : names){ FacesProperty prop = fdef.getProperty(name); if(prop instanceof FacesContainerProperty){ FacesContainerProperty cp = (FacesContainerProperty)prop; if(cp.getItemProperty() != null){ FacesProperty fp = cp.getItemProperty(); if(fp.getJavaClass().isAssignableFrom(ITreeNode.class)){ if (null == nameToFind || nameToFind.equals(name)) { member = classDef.getMember(name); break; } } } } } } } } catch(NodeException e) { if(ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.isErrorEnabled()){ ExtLibToolingLogger.EXT_LIB_TOOLING_LOGGER.error(e, StringUtil.format("Failed to get class definition or native class for {0}", ((Element)element).getTagName())); // $NLE-AbstractTreeNodePanel.Failedtogetclassdefinitionornativ-1$ } } } return member; } /* * (non-Javadoc) * @see com.ibm.designer.domino.xsp.api.panels.IPanelDataReciever#setExtraData(com.ibm.designer.domino.xsp.api.panels.IPanelExtraData) */ public void setExtraData(IPanelExtraData data) { this._data = data; } protected IPanelExtraData getExtraData() { return _data; } /* * (non-Javadoc) * @see com.ibm.designer.domino.xsp.api.panels.IPanelDataReciever#setExtraData(com.ibm.designer.domino.xsp.api.panels.IPanelExtraData) */ @Override protected void createContents() { this.getParent().addDisposeListener(new DisposeListener() { /* * (non-Javadoc) * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) */ public void widgetDisposed(DisposeEvent e) { if(!isDisposed()){ AbstractTreeNodePanel.this.dispose(); } } }); initLayout(); setCurrentParent(this); ExtLibPanelUtil.initDataNode(this, null, EXT_LIB_ATTR_CONFIGURATION); createTopSection(); createLinkButtons(); super.createContents(); } protected Composite createPanel(Composite parent, int columns) { Composite c = new Composite(this, SWT.NONE); GridLayout gl = SWTLayoutUtils.createLayoutNoMarginDefaultSpacing(columns); gl.marginWidth = gl.marginLeft = 0; c.setLayout(gl); GridData data = SWTLayoutUtils.createGDFillHorizontalNoGrab(); data.horizontalIndent = 0; data.horizontalSpan = 2; c.setLayoutData(data); return c; } private Composite _desc; protected Composite getDescriptionPanel() { if (null == _desc) { _desc = createPanel(this, 1); } return _desc; } protected void createTopSection() { String desc = getLinkAttributeDescription(); if(StringUtil.isNotEmpty(desc)){ Composite c = getDescriptionPanel(); Label l = new Label(c, SWT.NONE); GridData gd = new GridData(); gd.horizontalIndent = getControlIndentAmt(); l.setText(desc); l.setLayoutData(gd); } } protected void clearLinkSelection() { if (null != _linkViewer) { _linkViewer.setSelection(new StructuredSelection()); } } protected void enableButtons(boolean enabled) { if (null != _addLink) _addLink.setEnabled(enabled); if (null != _addChildLink) _addChildLink.setEnabled(false); /// selection should enable this when appropriate } protected void enableDelete(boolean enabled){ if(enabled && _linkViewer != null){ ISelection selection = _linkViewer.getSelection(); if(selection.isEmpty()){ enabled = false; //can't enable delete if there is nothing selected in the table } } if (null != _removeLink){ _removeLink.setEnabled(enabled); } } protected void updateViewers() { if (null != _linkViewer) { _linkViewer.refresh(); } if (null != _complexComposite) { _complexComposite.update(); } } protected void createLinkButtons() { CustomComposite buttonParent = new CustomComposite(this, SWT.NONE, "button.parent.id"); // $NON-NLS-1$ GridLayout gl = SWTLayoutUtils.createLayoutDefaultSpacing(3); gl.marginWidth = 0; buttonParent.setLayout(gl); GridData gd = SWTLayoutUtils.createGDFillHorizontal(); gd.horizontalSpan = 2; gd.horizontalIndent = getControlIndentAmt(); buttonParent.setLayoutData(gd); _addLink = new CustomButton(buttonParent, SWT.PUSH, "add.link.id"); // $NON-NLS-1$ _addLink.setText("Add Item"); // $NLX-AbstractTreeNodePanel.AddItem-1$ _addLink.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent event) { Button item = (Button) event.widget; Rectangle rect = item.getBounds(); Point pt = item.getParent().toDisplay(new Point(rect.x, rect.y)); _linkMenu.setLocation(pt.x, pt.y + rect.height); _linkMenu.setVisible(true); while (!_linkMenu.isDisposed() && _linkMenu.isVisible()) { if (!getDisplay().readAndDispatch()){ getDisplay().sleep(); } } } }); _addChildLink = new CustomButton(buttonParent, SWT.PUSH, "add.child.id"); // $NON-NLS-1$ _addChildLink.setText("Add Child"); // $NLX-AbstractTreeNodePanel.AddChild-1$ _addChildLink.setEnabled(false); // will enable on appropriate link selection _addChildLink.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { Button item = (Button) event.widget; Rectangle rect = item.getBounds(); Point pt = item.getParent().toDisplay(new Point(rect.x, rect.y)); _childLinkMenu.setLocation(pt.x, pt.y + rect.height); _childLinkMenu.setVisible(true); while (!_childLinkMenu.isDisposed() && _childLinkMenu.isVisible()) { if (!getDisplay().readAndDispatch()){ getDisplay().sleep(); } } } }); _removeLink = new CustomButton(buttonParent, SWT.PUSH, "remove.id"); // $NON-NLS-1$ _removeLink.setText("Remove"); // $NLX-AbstractTreeNodePanel.Remove-1$ _removeLink.setEnabled(false); // will enable on appropriate link selection _removeLink.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { AbstractTreeNodePanel.this.removeSelected(); } }); } protected void refreshMoveButtons(Element e){ if(_moveUp != null && !_moveUp.isDisposed()){ boolean enabled = canMoveUp(e); if(enabled){ _moveUp.setImage(getEnabledImage(true)); } else{ _moveUp.setImage(getDisabledImage(true)); } _moveUp.setEnabled(enabled); } if(_moveDown != null && !_moveDown.isDisposed()){ boolean enabled = canMoveDown(e); if(enabled){ _moveDown.setImage(getEnabledImage(false)); } else{ _moveDown.setImage(getDisabledImage(false)); } _moveDown.setEnabled(enabled); } } /** * Move the element down in the underlying model */ protected void moveDown(Element e) { Node nextSibling = XSPNodeUtil.getNextSibling(e); if (nextSibling != null) { nextSibling = XSPNodeUtil.getNextSibling(nextSibling); if(nextSibling != null){ e.getParentNode().insertBefore(e, nextSibling); } else{ e.getParentNode().appendChild(e); } XPagesDOMUtil.formatNode(e.getParentNode(), null); _linkViewer.setSelection(new StructuredSelection(e)); } } /** * Move the element up in the underlying model. */ protected void moveUp(Element e) { Node prevSibling = XSPNodeUtil.getPreviousSibling(e); if (prevSibling != null) { e.getParentNode().insertBefore(e, prevSibling); XPagesDOMUtil.formatNode(e.getParentNode(), null); _linkViewer.setSelection(new StructuredSelection(e)); } } protected boolean canMoveUp(Element e){ return XSPNodeUtil.getPreviousSibling(e) != null; } protected boolean canMoveDown(Element e){ return XSPNodeUtil.getNextSibling(e) != null; } private Image getDisabledImage(boolean upImage){ if(upImage){ return ExtLibToolingPlugin.getImage("moveUpDisabled.gif"); // $NON-NLS-1$ }else{ return ExtLibToolingPlugin.getImage("moveDownDisabled.gif"); // $NON-NLS-1$ } } private Image getEnabledImage(boolean upImage){ if(upImage){ return ExtLibToolingPlugin.getImage("moveUp.gif"); // $NON-NLS-1$ }else{ return ExtLibToolingPlugin.getImage("moveDown.gif"); // $NON-NLS-1$ } } protected TreeViewer getLinkTreeViewer(){ return _linkViewer; } /* (non-Javadoc) * @see com.ibm.commons.swt.data.layouts.PropLayout#dispose() */ @Override public void dispose() { IWorkbenchPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart(); IActionBars bars = null; if(part instanceof IViewPart){ bars = ((IViewPart)part).getViewSite().getActionBars(); if(bars != null){ bars.setGlobalActionHandler(ActionFactory.UNDO.getId(), null); bars.setGlobalActionHandler(ActionFactory.REDO.getId(), null); } } if(!isDisposed()){ super.dispose(); } } }