public void morph(Element element) throws DocumentValidationException { VEXDocument doc = this.getDocument(); int offset = this.getCaretOffset(); VEXElement currentElement = doc.getElementAt(offset); if (currentElement == doc.getRootElement()) { throw new DocumentValidationException("Cannot morph the root element."); } boolean success = false; try { this.beginWork(); this.moveTo(currentElement.getStartOffset() + 1, false); this.moveTo(currentElement.getEndOffset(), true); VEXDocumentFragment frag = this.getSelectedFragment(); this.deleteSelection(); this.moveBy(-1, false); this.moveBy(2, true); this.deleteSelection(); this.insertElement(element); if (frag != null) { this.insertFragment(frag); } this.moveTo(offset, false); success = true; } finally { this.endWork(success); } }
public boolean canUnwrap() { VEXDocument doc = this.getDocument(); if (doc == null) { return false; } Validator validator = doc.getValidator(); if (validator == null) { return false; } VEXElement element = doc.getElementAt(this.getCaretOffset()); VEXElement parent = element.getParent(); if (parent == null) { // can't unwrap the root return false; } EList<String> seq1 = doc.getNodeNames(parent.getStartOffset() + 1, element.getStartOffset()); EList<String> seq2 = doc.getNodeNames(element.getStartOffset() + 1, element.getEndOffset()); EList<String> seq3 = doc.getNodeNames(element.getEndOffset() + 1, parent.getEndOffset()); return validator.isValidSequence(parent.getName(), seq1, seq2, seq3, true); }
public void deleteNextChar() throws DocumentValidationException { if (this.hasSelection()) { this.deleteSelection(); } else { int offset = this.getCaretOffset(); VEXDocument doc = this.getDocument(); int n = doc.getLength() - 1; VEXElement element = doc.getElementAt(offset); if (offset == n) { // nop } else if (this.isBetweenMatchingElements(offset)) { this.joinElementsAt(offset); } else if (this.isBetweenMatchingElements(offset + 1)) { this.joinElementsAt(offset + 1); } else if (element.isEmpty()) { // deleting the right sentinel of an empty element // so just delete the whole element an move on this.moveTo(offset - 1, false); this.moveTo(offset + 1, true); this.deleteSelection(); } else if (doc.getElementAt(offset + 1).isEmpty()) { // deleting the left sentinel of an empty element // so just delete the whole element an move on this.moveTo(offset + 2, true); this.deleteSelection(); } else { if (doc.getCharacterAt(offset) != 0) { this.moveTo(offset, false); this.moveTo(offset + 1, true); this.deleteSelection(); } } } }
/** Returns true if text can be inserted at the current position. */ public boolean canInsertText() { VEXDocument doc = this.getDocument(); if (doc == null) { return false; } Validator validator = this.document.getValidator(); if (validator == null) { return true; } int startOffset = this.getCaretOffset(); int endOffset = this.getCaretOffset(); if (this.hasSelection()) { startOffset = this.getSelectionStart(); endOffset = this.getSelectionEnd(); } VEXElement parent = this.getDocument().getElementAt(startOffset); EList<String> seq1 = doc.getNodeNames(parent.getStartOffset() + 1, startOffset); EList<String> seq2 = new BasicEList<String>(); seq2.add(Validator.PCDATA); EList<String> seq3 = doc.getNodeNames(endOffset, parent.getEndOffset()); return validator.isValidSequence(parent.getName(), seq1, seq2, seq3, true); }
/** * Joins the elements at the given offset. Only works if isBetweenMatchingElements returns true * for the same offset. Afterwards, the caret is left at the point where the join occurred. * * @param offset Offset where the two elements meet. */ private void joinElementsAt(int offset) throws DocumentValidationException { if (!isBetweenMatchingElements(offset)) { throw new DocumentValidationException("Cannot join elements at offset " + offset); } boolean success = false; try { this.beginWork(); this.moveTo(offset + 1); VEXElement element = this.getCurrentElement(); boolean moveContent = !element.isEmpty(); VEXDocumentFragment frag = null; if (moveContent) { this.moveTo(element.getEndOffset(), true); frag = this.getSelectedFragment(); this.deleteSelection(); } this.moveBy(-1); this.moveBy(2, true); this.deleteSelection(); this.moveBy(-1); if (moveContent) { int savedOffset = this.getCaretOffset(); this.insertFragment(frag); this.moveTo(savedOffset, false); } success = true; } finally { this.endWork(success); } }
/** * Returns the start offset of the next sibling of the parent element. Returns -1 if there is no * previous sibling in the parent. * * @param vexWidget VexWidget to use. */ public static int getPreviousSiblingStart(IVexWidget vexWidget) { int startOffset; if (vexWidget.hasSelection()) { startOffset = vexWidget.getSelectionStart(); } else { Box box = vexWidget.findInnermostBox( new IBoxFilter() { public boolean matches(Box box) { return box instanceof BlockBox && box.getElement() != null; } }); if (box.getElement() == vexWidget.getDocument().getRootElement()) { return -1; } startOffset = box.getElement().getStartOffset(); } int previousSiblingStart = -1; VEXElement parent = vexWidget.getDocument().getElementAt(startOffset); List<VEXNode> children = parent.getChildNodes(); for (VEXNode child : children) { if (startOffset == child.getStartOffset()) { break; } previousSiblingStart = child.getStartOffset(); } return previousSiblingStart; }
public void removeAttribute(String attributeName) { try { VEXElement element = this.getCurrentElement(); if (element.getAttribute(attributeName) != null) { element.removeAttribute(attributeName); } } catch (DocumentValidationException ex) { ex.printStackTrace(); // TODO: when can this happen? } }
public void split() throws DocumentValidationException { long start = System.currentTimeMillis(); VEXDocument doc = this.getDocument(); VEXElement element = doc.getElementAt(this.getCaretOffset()); Styles styles = this.getStyleSheet().getStyles(element); while (!styles.isBlock()) { element = element.getParent(); styles = this.getStyleSheet().getStyles(element); } boolean success = false; try { this.beginWork(); if (styles.getWhiteSpace().equals(CSS.PRE)) { // can't call this.insertText() or we'll get an infinite loop int offset = this.getCaretOffset(); doc.insertText(offset, "\n"); this.moveTo(offset + 1); } else { VEXDocumentFragment frag = null; int offset = this.getCaretOffset(); boolean atEnd = (offset == element.getEndOffset()); if (!atEnd) { this.moveTo(element.getEndOffset(), true); frag = this.getSelectedFragment(); this.deleteSelection(); } // either way, we are now at the end offset for the element // let's move just outside this.moveTo(this.getCaretOffset() + 1); this.insertElement(new Element(element.getName())); // TODO: clone attributes if (!atEnd) { offset = this.getCaretOffset(); this.insertFragment(frag); this.moveTo(offset, false); } } success = true; } finally { this.endWork(success); } if (this.isDebugging()) { long end = System.currentTimeMillis(); System.out.println("split() took " + (end - start) + "ms"); } }
public void setAttribute(String attributeName, String value) { try { VEXElement element = this.getCurrentElement(); if (value == null) { this.removeAttribute(attributeName); } else if (!value.equals(element.getAttribute(attributeName))) { element.setAttribute(attributeName, value); } } catch (DocumentValidationException ex) { ex.printStackTrace(); // TODO: mebbe throw the exception instead } }
public String[] getValidInsertElements() { VEXDocument doc = this.getDocument(); if (doc == null) return new String[0]; Validator validator = doc.getValidator(); if (validator == null) return new String[0]; int startOffset = this.getCaretOffset(); int endOffset = this.getCaretOffset(); if (this.hasSelection()) { startOffset = this.getSelectionStart(); endOffset = this.getSelectionEnd(); } VEXElement parent = doc.getElementAt(startOffset); Set<String> validItems = validator.getValidItems(parent.getName()); List<String> candidates = new ArrayList<String>(validItems); candidates.remove(Validator.PCDATA); // filter invalid sequences EList<String> nodesBefore = doc.getNodeNames(parent.getStartOffset() + 1, startOffset); EList<String> nodesAfter = doc.getNodeNames(endOffset, parent.getEndOffset()); int sequenceLength = nodesBefore.size() + 1 + nodesAfter.size(); for (Iterator<String> iter = candidates.iterator(); iter.hasNext(); ) { String candidate = iter.next(); EList<String> sequence = new BasicEList<String>(sequenceLength); sequence.addAll(nodesBefore); sequence.add(candidate); sequence.addAll(nodesAfter); if (!validator.isValidSequence(parent.getName(), sequence, true)) { iter.remove(); } } // If there's a selection, root out those candidates that can't // contain it. if (hasSelection()) { EList<String> selectedNodes = doc.getNodeNames(startOffset, endOffset); for (Iterator<String> iter = candidates.iterator(); iter.hasNext(); ) { String candidate = iter.next(); if (!validator.isValidSequence(candidate, selectedNodes, true)) { iter.remove(); } } } Collections.sort(candidates); return (String[]) candidates.toArray(new String[candidates.size()]); }
/** * Iterate over all rows in the table containing the caret. * * @param vexWidget IVexWidget to iterate over. * @param callback Caller-provided callback that this method calls for each row in the current * table. */ public static void iterateTableRows(IVexWidget vexWidget, ElementOrRangeCallback callback) { final StyleSheet ss = vexWidget.getStyleSheet(); final VEXDocument doc = vexWidget.getDocument(); final int offset = vexWidget.getCaretOffset(); // This may or may not be a table // In any case, it's the element that contains the top-level table // children VEXElement table = doc.getElementAt(offset); while (table != null && !LayoutUtils.isTableChild(ss, table)) { table = table.getParent(); } while (table != null && LayoutUtils.isTableChild(ss, table)) { table = table.getParent(); } if (table == null || table.getParent() == null) { return; } final List<Element> tableChildren = new ArrayList<Element>(); final boolean[] found = new boolean[] {false}; LayoutUtils.iterateChildrenByDisplayStyle( ss, LayoutUtils.TABLE_CHILD_STYLES, table, new ElementOrRangeCallback() { public void onElement(Element child, String displayStyle) { if (offset >= child.getStartOffset() && offset <= child.getEndOffset()) { found[0] = true; } tableChildren.add(child); } public void onRange(VEXElement parent, int startOffset, int endOffset) { if (!found[0]) { tableChildren.clear(); } } }); if (!found[0]) { return; } int startOffset = tableChildren.get(0).getStartOffset(); int endOffset = tableChildren.get(tableChildren.size() - 1).getEndOffset() + 1; LayoutUtils.iterateTableRows(ss, table, startOffset, endOffset, callback); }
/** * Returns the innermost Element with style table-row containing the caret, or null if no such * element exists. * * @param vexWidget IVexWidget to use. */ public static VEXElement getCurrentTableRow(IVexWidget vexWidget) { StyleSheet ss = vexWidget.getStyleSheet(); VEXElement element = vexWidget.getCurrentElement(); while (element != null) { if (ss.getStyles(element).getDisplay().equals(CSS.TABLE_ROW)) { return element; } element = element.getParent(); } return null; }
public String[] getValidMorphElements() { VEXDocument doc = this.getDocument(); if (doc == null) return new String[0]; Validator validator = doc.getValidator(); if (validator == null) return new String[0]; VEXElement element = doc.getElementAt(this.getCaretOffset()); VEXElement parent = element.getParent(); if (parent == null) { // can't morph the root return new String[0]; } Set<String> validItems = validator.getValidItems(parent.getName()); List<String> result = new ArrayList<String>(validItems); result.remove(Validator.PCDATA); result.remove(element.getName()); // exclude converting to the same // root out those that can't contain the current content EList<String> content = doc.getNodeNames(element.getStartOffset() + 1, element.getEndOffset()); for (Iterator<String> iter = result.iterator(); iter.hasNext(); ) { String candidate = iter.next(); if (!validator.isValidSequence(candidate, content, true)) { iter.remove(); } } Collections.sort(result); return result.toArray(new String[result.size()]); }
/** * Returns true if the given offset represents the boundary between two different elements with * the same name and parent. This is used to determine if the elements can be joined via * joinElementsAt. * * @param int offset The offset to check. */ private boolean isBetweenMatchingElements(int offset) { if (offset <= 1 || offset >= this.getDocument().getLength() - 1) { return false; } VEXElement e1 = this.getDocument().getElementAt(offset - 1); VEXElement e2 = this.getDocument().getElementAt(offset + 1); return e1 != e2 && e1.getParent() == e2.getParent() && e1.getName().equals(e2.getName()); }
public void testSetElementName() throws Exception { VEXElement element = getFixture(); element.setName("book"); assertEquals("Element name not set", "book", element.getName()); }
/** * Tests the '{@link * org.eclipse.wst.xml.vex.core.internal.provisional.dom.I.VEXElement#setNamespace(java.lang.String, * java.lang.String) <em>Set Namespace</em>}' operation. * <!-- begin-user-doc --> * <!-- end-user-doc --> * * @see * org.eclipse.wst.xml.vex.core.internal.provisional.dom.I.VEXElement#setNamespace(java.lang.String, * java.lang.String) * @generated */ public void testSetNamespace__String_String() { VEXElement element = getFixture(); element.setNamespace("docbook", "http://www.docbook.org/"); assertEquals("Prefix wrong:", "docbook", element.getNamespacePrefix()); assertEquals("Namespace wrong:", "http://www.docbook.org/", element.getNamespaceURI()); }
public void moveTo(int offset, boolean select) { if (offset >= 1 && offset <= this.document.getLength() - 1) { // repaint the selection area, if any this.repaintCaret(); this.repaintRange(this.getSelectionStart(), this.getSelectionEnd()); VEXElement oldElement = this.currentElement; this.caretOffset = offset; this.currentElement = this.document.getElementAt(offset); if (select) { this.selectionStart = Math.min(this.mark, this.caretOffset); this.selectionEnd = Math.max(this.mark, this.caretOffset); // move selectionStart and selectionEnd to make sure we don't // select a partial element VEXElement commonElement = this.document.findCommonElement(this.selectionStart, this.selectionEnd); VEXElement element = this.document.getElementAt(this.selectionStart); while (element != commonElement) { this.selectionStart = element.getStartOffset(); element = this.document.getElementAt(this.selectionStart); } element = this.document.getElementAt(this.selectionEnd); while (element != commonElement) { this.selectionEnd = element.getEndOffset() + 1; element = this.document.getElementAt(this.selectionEnd); } } else { this.mark = offset; this.selectionStart = offset; this.selectionEnd = offset; } if (this.beginWorkCount == 0) { this.relayout(); } Graphics g = this.hostComponent.createDefaultGraphics(); LayoutContext context = this.createLayoutContext(g); this.caret = this.rootBox.getCaret(context, offset); VEXElement element = this.getCurrentElement(); if (element != oldElement) { this.caretColor = Color.BLACK; while (element != null) { Color bgColor = this.styleSheet.getStyles(element).getBackgroundColor(); if (bgColor != null) { int red = ~bgColor.getRed() & 0xff; int green = ~bgColor.getGreen() & 0xff; int blue = ~bgColor.getBlue() & 0xff; this.caretColor = new Color(red, green, blue); break; } element = element.getParent(); } } g.dispose(); this.magicX = -1; this.scrollCaretVisible(); this.hostComponent.fireSelectionChanged(); this.caretVisible = true; this.repaintRange(this.getSelectionStart(), this.getSelectionEnd()); } }