| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.sling.ide.eclipse.ui.actions; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.LinkedList; |
| |
| import javax.jcr.nodetype.NodeDefinition; |
| import javax.jcr.nodetype.NodeType; |
| |
| import org.apache.sling.ide.eclipse.ui.nav.model.JcrNode; |
| import org.apache.sling.ide.transport.NodeTypeRegistry; |
| import org.apache.sling.ide.transport.RepositoryException; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.InputDialog; |
| import org.eclipse.jface.fieldassist.ComboContentAdapter; |
| import org.eclipse.jface.fieldassist.ContentProposalAdapter; |
| import org.eclipse.jface.fieldassist.SimpleContentProposalProvider; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.ModifyEvent; |
| import org.eclipse.swt.events.ModifyListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Combo; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Shell; |
| |
| public class NewNodeDialog extends InputDialog { |
| |
| private static String lastChosenNodeType = null; |
| |
| private final String parentNodeType; |
| private final NodeTypeRegistry ntManager; |
| protected String comboSelection; |
| private Collection<String> allowedChildren; |
| private Combo combo; |
| private ContentProposalAdapter proposalAdapter; |
| private NodeDefinition[] allChildNodeDefs; |
| |
| public NewNodeDialog(Shell parentShell, JcrNode node, |
| NodeTypeRegistry ntManager) throws RepositoryException { |
| super(parentShell, "Enter Node Name", |
| "Enter name for new node under:\n path: "+node.getJcrPath(), "", null); |
| this.parentNodeType = node.getPrimaryType(); |
| this.ntManager = ntManager; |
| if (ntManager!=null) { |
| final LinkedList<String> ac = new LinkedList<>(ntManager.getAllowedPrimaryChildNodeTypes(parentNodeType)); |
| final NodeType parentNt = ntManager.getNodeType(parentNodeType); |
| allChildNodeDefs = parentNt.getChildNodeDefinitions(); |
| Collections.sort(ac); |
| this.allowedChildren = ac; |
| } |
| } |
| |
| @Override |
| protected Control createDialogArea(Composite parent) { |
| Composite composite = (Composite) super.createDialogArea(parent); |
| |
| Control[] children = composite.getChildren(); |
| Control errorMessageText = children[children.length-1]; |
| GridData errorMessageGridData = new GridData(GridData.GRAB_HORIZONTAL |
| | GridData.HORIZONTAL_ALIGN_FILL); |
| errorMessageGridData.heightHint = convertHeightInCharsToPixels(2); |
| errorMessageText.setLayoutData(errorMessageGridData); |
| |
| // now add the node type dropbox-combo |
| Label label = new Label(composite, SWT.WRAP); |
| label.moveAbove(errorMessageText); |
| label.setText("Define node type"); |
| GridData data = new GridData(GridData.GRAB_HORIZONTAL |
| | GridData.GRAB_VERTICAL | GridData.HORIZONTAL_ALIGN_FILL |
| | GridData.VERTICAL_ALIGN_CENTER); |
| data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH); |
| label.setLayoutData(data); |
| label.setFont(parent.getFont()); |
| |
| combo = new Combo(composite, SWT.DROP_DOWN); |
| combo.moveAbove(errorMessageText); |
| if (allowedChildren!=null) { |
| combo.setItems(allowedChildren.toArray(new String[0])); |
| } |
| combo.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL |
| | GridData.HORIZONTAL_ALIGN_FILL)); |
| combo.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| comboSelection = combo.getText(); |
| validateInput(); |
| } |
| }); |
| combo.addModifyListener(new ModifyListener() { |
| |
| @Override |
| public void modifyText(ModifyEvent e) { |
| comboSelection = combo.getText(); |
| validateInput(); |
| } |
| |
| }); |
| |
| SimpleContentProposalProvider proposalProvider = new SimpleContentProposalProvider(combo.getItems()); |
| proposalProvider.setFiltering(true); |
| final ComboContentAdapter controlContentAdapter = new ComboContentAdapter() { |
| @Override |
| public void insertControlContents(Control control, String text, |
| int cursorPosition) { |
| Point selection = combo.getSelection(); |
| combo.setText(text); |
| selection.x = selection.x + cursorPosition; |
| selection.y = selection.x; |
| combo.setSelection(selection); |
| } |
| |
| @Override |
| public Rectangle getInsertionBounds(Control control) { |
| final Rectangle insertionBounds = super.getInsertionBounds(control); |
| // always insert at start |
| insertionBounds.x = 0; |
| insertionBounds.y = 0; |
| return insertionBounds; |
| } |
| |
| |
| }; |
| // this variant opens auto-complete on each character |
| proposalAdapter = new ContentProposalAdapter(combo, controlContentAdapter, proposalProvider, null, null); |
| // this variant opens auto-complete only when invoking the auto-complete hotkey |
| if (allowedChildren!=null && allowedChildren.size()==1) { |
| combo.setText(allowedChildren.iterator().next()); |
| } else if (allowedChildren!=null) { |
| if (allowedChildren.contains(lastChosenNodeType)) { |
| combo.setText(lastChosenNodeType); |
| } |
| } |
| |
| return composite; |
| } |
| |
| @Override |
| protected void initializeBounds() { |
| super.initializeBounds(); |
| // fix autocomplete popup size: |
| Point size = combo.getSize(); |
| size.x /= 2; |
| size.y = 180; |
| proposalAdapter.setPopupSize(size); |
| } |
| |
| public String getChosenNodeType() { |
| lastChosenNodeType = comboSelection; |
| return comboSelection; |
| } |
| |
| protected void validateInput() { |
| final String firstInput = getText().getText(); |
| final String secondInput = comboSelection; |
| try { |
| if (secondInput==null || secondInput.length()==0) { |
| setErrorMessage(""); |
| } else if (ntManager==null) { |
| setErrorMessage(null); |
| } else if (ntManager.isAllowedPrimaryChildNodeType(parentNodeType, secondInput)) { |
| // also check on the name, not only the type |
| if (allChildNodeDefs==null) { |
| setErrorMessage("No child node definitions found for "+parentNodeType); |
| } else { |
| boolean success = false; |
| for (int i = 0; i < allChildNodeDefs.length; i++) { |
| NodeDefinition aChildNodeDef = allChildNodeDefs[i]; |
| if (aChildNodeDef.getName()!=null && aChildNodeDef.getName().length()>0) { |
| if (firstInput.equals(aChildNodeDef.getName())) { |
| setErrorMessage(null); |
| return; |
| } |
| } else { |
| // mark success if there's a child node definition without a name |
| // (ie then it can be any name) |
| success = true; |
| } |
| } |
| if (success) { |
| setErrorMessage(null); |
| return; |
| } |
| StringBuffer details = new StringBuffer(); |
| for (NodeDefinition aChildNodeDef : allChildNodeDefs) { |
| if (details.length()!=0) { |
| details.append(", "); |
| } |
| details.append("(name="+aChildNodeDef.getName()+", required primary type(s)="); |
| String[] requiredPrimaryTypeNames = aChildNodeDef.getRequiredPrimaryTypeNames(); |
| if (requiredPrimaryTypeNames==null) { |
| details.append("null"); |
| } else { |
| for (int j = 0; j < requiredPrimaryTypeNames.length; j++) { |
| String rptn = requiredPrimaryTypeNames[j]; |
| if (j>0) { |
| details.append(","); |
| } |
| details.append(rptn); |
| } |
| } |
| details.append(")"); |
| } |
| setErrorMessage("No matching child node definition found for "+parentNodeType+". Expected one of: "+details); |
| } |
| } else { |
| setErrorMessage("Error: Invalid child node type of "+parentNodeType); |
| } |
| } catch(RepositoryException e) { |
| setErrorMessage("RepositoryException: "+e); |
| } |
| |
| }; |
| } |