// set up in advance... break; default: // only those three values of direction allowed... throw new AssertionError(direction); } // set the unset edge; if neither set then we're getting // both edges of the current run around our 'index' attributeRunStartIndex = (attributeRunStartIndex != Integer.MIN_VALUE) ? attributeRunStartIndex : getRunEdge(tempIndex, -1); attributeRunEndIndex = (attributeRunEndIndex != Integer.MIN_VALUE) ? attributeRunEndIndex : getRunEdge(tempIndex, 1); runText = model.getText(attributeRunStartIndex, attributeRunEndIndex - attributeRunStartIndex); } catch (BadLocationException e) { // we are intentionally silent; our contract says we return // null if there is any failure in this method return null; } finally { if (model instanceof AbstractDocument) { ((AbstractDocument)model).readUnlock(); } } return new AccessibleTextSequence(attributeRunStartIndex, attributeRunEndIndex, runText); default: break; } return null; } /** * Starting at text position <code>index</code>, and going in * <code>direction</code>, return the edge of run that shares the * same <code>AttributeSet</code> and parent element as those at * <code>index</code>. * * Note: we assume the document is already locked... */ private int getRunEdge(int index, int direction) throws BadLocationException { if (index < 0 || index >= model.getLength()) { throw new BadLocationException("Location out of bounds", index); } // locate the Element at index Element indexElement; // locate the Element at our index/offset int elementIndex = -1; // test for initialization for (indexElement = model.getDefaultRootElement(); ! indexElement.isLeaf(); ) { elementIndex = indexElement.getElementIndex(index); indexElement = indexElement.getElement(elementIndex); } if (elementIndex == -1) { throw new AssertionError(index); } // cache the AttributeSet and parentElement atindex AttributeSet indexAS = indexElement.getAttributes(); Element parent = indexElement.getParentElement(); // find the first Element before/after ours w/the same AttributeSet // if we are already at edge of the first element in our parent // then return that edge Element edgeElement; switch (direction) { case -1: case 1: int edgeElementIndex = elementIndex; int elementCount = parent.getElementCount(); while ((edgeElementIndex + direction) > 0 && ((edgeElementIndex + direction) < elementCount) && parent.getElement(edgeElementIndex + direction).getAttributes().isEqual(indexAS)) { edgeElementIndex += direction; } edgeElement = parent.getElement(edgeElementIndex); break; default: throw new AssertionError(direction); } switch (direction) { case -1: return edgeElement.getStartOffset(); case 1: return edgeElement.getEndOffset(); default: // we already caught this case earlier; this is to satisfy // the compiler... return Integer.MIN_VALUE; } } // getTextRange() not needed; defined in AccessibleEditableText /** * Returns the <code>AccessibleTextSequence</code> at a given * <code>index</code>. * * @param part the <code>CHARACTER</code>, <code>WORD</code>, * <code>SENTENCE</code>, <code>LINE</code> or * <code>ATTRIBUTE_RUN</code> to retrieve * @param index an index within the text * @return an <code>AccessibleTextSequence</code> specifying the text if * <code>part</code> and <code>index</code> are valid. Otherwise, * <code>null</code> is returned * * @see javax.accessibility.AccessibleText#CHARACTER * @see javax.accessibility.AccessibleText#WORD * @see javax.accessibility.AccessibleText#SENTENCE * @see javax.accessibility.AccessibleExtendedText#LINE * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN * * @since 1.6 */ public AccessibleTextSequence getTextSequenceAt(int part, int index) { return getSequenceAtIndex(part, index, 0); } /** * Returns the <code>AccessibleTextSequence</code> after a given * <code>index</code>. * * @param part the <code>CHARACTER</code>, <code>WORD</code>, * <code>SENTENCE</code>, <code>LINE</code> or * <code>ATTRIBUTE_RUN</code> to retrieve * @param index an index within the text * @return an <code>AccessibleTextSequence</code> specifying the text * if <code>part</code> and <code>index</code> are valid. Otherwise, * <code>null</code> is returned * * @see javax.accessibility.AccessibleText#CHARACTER * @see javax.accessibility.AccessibleText#WORD * @see javax.accessibility.AccessibleText#SENTENCE * @see javax.accessibility.AccessibleExtendedText#LINE * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN * * @since 1.6 */ public AccessibleTextSequence getTextSequenceAfter(int part, int index) { return getSequenceAtIndex(part, index, 1); } /** * Returns the <code>AccessibleTextSequence</code> before a given * <code>index</code>. * * @param part the <code>CHARACTER</code>, <code>WORD</code>, * <code>SENTENCE</code>, <code>LINE</code> or * <code>ATTRIBUTE_RUN</code> to retrieve * @param index an index within the text * @return an <code>AccessibleTextSequence</code> specifying the text * if <code>part</code> and <code>index</code> are valid. Otherwise, * <code>null</code> is returned * * @see javax.accessibility.AccessibleText#CHARACTER * @see javax.accessibility.AccessibleText#WORD * @see javax.accessibility.AccessibleText#SENTENCE * @see javax.accessibility.AccessibleExtendedText#LINE * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN * * @since 1.6 */ public AccessibleTextSequence getTextSequenceBefore(int part, int index) { return getSequenceAtIndex(part, index, -1); } /** * Returns the <code>Rectangle</code> enclosing the text between * two indicies. * * @param startIndex the start index in the text * @param endIndex the end index in the text * @return the bounding rectangle of the text if the indices are valid. * Otherwise, <code>null</code> is returned * * @since 1.6 */ public Rectangle getTextBounds(int startIndex, int endIndex) { if (startIndex < 0 || startIndex > model.getLength()-1 || endIndex < 0 || endIndex > model.getLength()-1 || startIndex > endIndex) { return null; } TextUI ui = getUI(); if (ui == null) { return null; } Rectangle rect = null; Rectangle alloc = getRootEditorRect(); if (alloc == null) { return null; } if (model instanceof AbstractDocument) { ((AbstractDocument)model).readLock(); } try { View rootView = ui.getRootView(JTextComponent.this); if (rootView != null) { Shape bounds = rootView.modelToView(startIndex, Position.Bias.Forward, endIndex, Position.Bias.Backward, alloc); rect = (bounds instanceof Rectangle) ? (Rectangle)bounds : bounds.getBounds(); } } catch (BadLocationException e) { } finally { if (model instanceof AbstractDocument) { ((AbstractDocument)model).readUnlock(); } } return rect; } // ----- end AccessibleExtendedText methods // --- interface AccessibleAction methods ------------------------ public AccessibleAction getAccessibleAction() { return this; } /** * Returns the number of accessible actions available in this object * If there are more than one, the first one is considered the * "default" action of the object. * * @return the zero-based number of Actions in this object * @since 1.4 */ public int getAccessibleActionCount() { Action [] actions = JTextComponent.this.getActions(); return actions.length; } /** * Returns a description of the specified action of the object. * * @param i zero-based index of the actions * @return a String description of the action * @see #getAccessibleActionCount * @since 1.4 */ public String getAccessibleActionDescription(int i) { Action [] actions = JTextComponent.this.getActions(); if (i < 0 || i >= actions.length) { return null; } return (String)actions[i].getValue(Action.NAME); } /** * Performs the specified Action on the object * * @param i zero-based index of actions * @return true if the action was performed; otherwise false. * @see #getAccessibleActionCount * @since 1.4 */ public boolean doAccessibleAction(int i) { Action [] actions = JTextComponent.this.getActions(); if (i < 0 || i >= actions.length) { return false; } ActionEvent ae = new ActionEvent(JTextComponent.this, ActionEvent.ACTION_PERFORMED, null, EventQueue.getMostRecentEventTime(), getCurrentEventModifiers()); actions[i].actionPerformed(ae); return true; } // ----- end AccessibleAction methods } // --- serialization --------------------------------------------- private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); caretEvent = new MutableCaretEvent(this); addMouseListener(caretEvent); addFocusListener(caretEvent); } // --- member variables ---------------------------------- /** * The document model. */ private Document model; /** * The caret used to display the insert position * and navigate throughout the document. * * PENDING(prinz) * This should be serializable, default installed * by UI. */ private transient Caret caret; /** * Object responsible for restricting the cursor navigation. */ private NavigationFilter navigationFilter; /** * The object responsible for managing highlights. * * PENDING(prinz) * This should be serializable, default installed * by UI. */ private transient Highlighter highlighter; /** * The current key bindings in effect. * * PENDING(prinz) * This should be serializable, default installed * by UI. */ private transient Keymap keymap; private transient MutableCaretEvent caretEvent; private Color caretColor; private Color selectionColor; private Color selectedTextColor; private Color disabledTextColor; private boolean editable; private Insets margin; private char focusAccelerator; private boolean dragEnabled; /** * The drop mode for this component. */ private DropMode dropMode = DropMode.USE_SELECTION; /** * The drop location. */ private transient DropLocation dropLocation; /** * Represents a drop location for <code>JTextComponent</code>s. * * @see #getDropLocation * @since 1.6 */ public static final class DropLocation extends TransferHandler.DropLocation { private final int index; private final Position.Bias bias; private DropLocation(Point p, int index, Position.Bias bias) { super(p); this.index = index; this.bias = bias; } /** * Returns the index where dropped data should be inserted into the * associated component. This index represents a position between * characters, as would be interpreted by a caret. * * @return the drop index */ public int getIndex() { return index; } /** * Returns the bias for the drop index. * * @return the drop bias */ public Position.Bias getBias() { return bias; } /** * Returns a string representation of this drop location. * This method is intended to be used for debugging purposes, * and the content and format of the returned string may vary * between implementations. * * @return a string representation of this drop location */ public String toString() { return getClass().getName() + "[dropPoint=" + getDropPoint() + "," + "index=" + index + "," + "bias=" + bias + "]"; } } /** * TransferHandler used if one hasn't been supplied by the UI. */ private static DefaultTransferHandler defaultTransferHandler; /** * Maps from class name to Boolean indicating if * <code>processInputMethodEvent</code> has been overriden. */ private static Map<String, Boolean> overrideMap; /** * Returns a string representation of this <code>JTextComponent</code>. * This method is intended to be used only for debugging purposes, and the * content and format of the returned string may vary between * implementations. The returned string may be empty but may not * be <code>null</code>. * <P> * Overriding <code>paramString</code> to provide information about the * specific new aspects of the JFC components. * * @return a string representation of this <code>JTextComponent</code> */ protected String paramString() { String editableString = (editable ? "true" : "false"); String caretColorString = (caretColor != null ? caretColor.toString() : ""); String selectionColorString = (selectionColor != null ? selectionColor.toString() : ""); String selectedTextColorString = (selectedTextColor != null ? selectedTextColor.toString() : ""); String disabledTextColorString = (disabledTextColor != null ? disabledTextColor.toString() : ""); String marginString = (margin != null ? margin.toString() : ""); return super.paramString() + ",caretColor=" + caretColorString + ",disabledTextColor=" + disabledTextColorString + ",editable=" + editableString + ",margin=" + marginString + ",selectedTextColor=" + selectedTextColorString + ",selectionColor=" + selectionColorString; } /** * A Simple TransferHandler that exports the data as a String, and * imports the data from the String clipboard. This is only used * if the UI hasn't supplied one, which would only happen if someone * hasn't subclassed Basic. */ static class DefaultTransferHandler extends TransferHandler implements UIResource { public void exportToClipboard(JComponent comp, Clipboard clipboard, int action) throws IllegalStateException { if (comp instanceof JTextComponent) { JTextComponent text = (JTextComponent)comp; int p0 = text.getSelectionStart(); int p1 = text.getSelectionEnd(); if (p0 != p1) { try { Document doc = text.getDocument(); String srcData = doc.getText(p0, p1 - p0); StringSelection contents =new StringSelection(srcData); // this may throw an IllegalStateException, // but it will be caught and handled in the // action that invoked this method clipboard.setContents(contents, null); if (action == TransferHandler.MOVE) { doc.remove(p0, p1 - p0); } } catch (BadLocationException ble) {} } } } public boolean importData(JComponent comp, Transferable t) { if (comp instanceof JTextComponent) { DataFlavor flavor = getFlavor(t.getTransferDataFlavors()); if (flavor != null) { InputContext ic = comp.getInputContext(); if (ic != null) { ic.endComposition(); }Lines 3501–4000 of 5073. Previous lines Load more
// set up in advance... break; default: // only those three values of direction allowed... throw new AssertionError(direction); } // set the unset edge; if neither set then we're getting // both edges of the current run around our 'index' attributeRunStartIndex = (attributeRunStartIndex != Integer.MIN_VALUE) ? attributeRunStartIndex : getRunEdge(tempIndex, -1); attributeRunEndIndex = (attributeRunEndIndex != Integer.MIN_VALUE) ? attributeRunEndIndex : getRunEdge(tempIndex, 1); runText = model.getText(attributeRunStartIndex, attributeRunEndIndex - attributeRunStartIndex); } catch (BadLocationException e) { // we are intentionally silent; our contract says we return // null if there is any failure in this method return null; } finally { if (model instanceof AbstractDocument) { ((AbstractDocument)model).readUnlock(); } } return new AccessibleTextSequence(attributeRunStartIndex, attributeRunEndIndex, runText); default: break; } return null; } /** * Starting at text position <code>index</code>, and going in * <code>direction</code>, return the edge of run that shares the * same <code>AttributeSet</code> and parent element as those at * <code>index</code>. * * Note: we assume the document is already locked... */ private int getRunEdge(int index, int direction) throws BadLocationException { if (index < 0 || index >= model.getLength()) { throw new BadLocationException("Location out of bounds", index); } // locate the Element at index Element indexElement; // locate the Element at our index/offset int elementIndex = -1; // test for initialization for (indexElement = model.getDefaultRootElement(); ! indexElement.isLeaf(); ) { elementIndex = indexElement.getElementIndex(index); indexElement = indexElement.getElement(elementIndex); } if (elementIndex == -1) { throw new AssertionError(index); } // cache the AttributeSet and parentElement atindex AttributeSet indexAS = indexElement.getAttributes(); Element parent = indexElement.getParentElement(); // find the first Element before/after ours w/the same AttributeSet // if we are already at edge of the first element in our parent // then return that edge Element edgeElement; switch (direction) { case -1: case 1: int edgeElementIndex = elementIndex; int elementCount = parent.getElementCount(); while ((edgeElementIndex + direction) > 0 && ((edgeElementIndex + direction) < elementCount) && parent.getElement(edgeElementIndex + direction).getAttributes().isEqual(indexAS)) { edgeElementIndex += direction; } edgeElement = parent.getElement(edgeElementIndex); break; default: throw new AssertionError(direction); } switch (direction) { case -1: return edgeElement.getStartOffset(); case 1: return edgeElement.getEndOffset(); default: // we already caught this case earlier; this is to satisfy // the compiler... return Integer.MIN_VALUE; } } // getTextRange() not needed; defined in AccessibleEditableText /** * Returns the <code>AccessibleTextSequence</code> at a given * <code>index</code>. * * @param part the <code>CHARACTER</code>, <code>WORD</code>, * <code>SENTENCE</code>, <code>LINE</code> or * <code>ATTRIBUTE_RUN</code> to retrieve * @param index an index within the text * @return an <code>AccessibleTextSequence</code> specifying the text if * <code>part</code> and <code>index</code> are valid. Otherwise, * <code>null</code> is returned * * @see javax.accessibility.AccessibleText#CHARACTER * @see javax.accessibility.AccessibleText#WORD * @see javax.accessibility.AccessibleText#SENTENCE * @see javax.accessibility.AccessibleExtendedText#LINE * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN * * @since 1.6 */ public AccessibleTextSequence getTextSequenceAt(int part, int index) { return getSequenceAtIndex(part, index, 0); } /** * Returns the <code>AccessibleTextSequence</code> after a given * <code>index</code>. * * @param part the <code>CHARACTER</code>, <code>WORD</code>, * <code>SENTENCE</code>, <code>LINE</code> or * <code>ATTRIBUTE_RUN</code> to retrieve * @param index an index within the text * @return an <code>AccessibleTextSequence</code> specifying the text * if <code>part</code> and <code>index</code> are valid. Otherwise, * <code>null</code> is returned * * @see javax.accessibility.AccessibleText#CHARACTER * @see javax.accessibility.AccessibleText#WORD * @see javax.accessibility.AccessibleText#SENTENCE * @see javax.accessibility.AccessibleExtendedText#LINE * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN * * @since 1.6 */ public AccessibleTextSequence getTextSequenceAfter(int part, int index) { return getSequenceAtIndex(part, index, 1); } /** * Returns the <code>AccessibleTextSequence</code> before a given * <code>index</code>. * * @param part the <code>CHARACTER</code>, <code>WORD</code>, * <code>SENTENCE</code>, <code>LINE</code> or * <code>ATTRIBUTE_RUN</code> to retrieve * @param index an index within the text * @return an <code>AccessibleTextSequence</code> specifying the text * if <code>part</code> and <code>index</code> are valid. Otherwise, * <code>null</code> is returned * * @see javax.accessibility.AccessibleText#CHARACTER * @see javax.accessibility.AccessibleText#WORD * @see javax.accessibility.AccessibleText#SENTENCE * @see javax.accessibility.AccessibleExtendedText#LINE * @see javax.accessibility.AccessibleExtendedText#ATTRIBUTE_RUN * * @since 1.6 */ public AccessibleTextSequence getTextSequenceBefore(int part, int index) { return getSequenceAtIndex(part, index, -1); } /** * Returns the <code>Rectangle</code> enclosing the text between * two indicies. * * @param startIndex the start index in the text * @param endIndex the end index in the text * @return the bounding rectangle of the text if the indices are valid. * Otherwise, <code>null</code> is returned * * @since 1.6 */ public Rectangle getTextBounds(int startIndex, int endIndex) { if (startIndex < 0 || startIndex > model.getLength()-1 || endIndex < 0 || endIndex > model.getLength()-1 || startIndex > endIndex) { return null; } TextUI ui = getUI(); if (ui == null) { return null; } Rectangle rect = null; Rectangle alloc = getRootEditorRect(); if (alloc == null) { return null; } if (model instanceof AbstractDocument) { ((AbstractDocument)model).readLock(); } try { View rootView = ui.getRootView(JTextComponent.this); if (rootView != null) { Shape bounds = rootView.modelToView(startIndex, Position.Bias.Forward, endIndex, Position.Bias.Backward, alloc); rect = (bounds instanceof Rectangle) ? (Rectangle)bounds : bounds.getBounds(); } } catch (BadLocationException e) { } finally { if (model instanceof AbstractDocument) { ((AbstractDocument)model).readUnlock(); } } return rect; } // ----- end AccessibleExtendedText methods // --- interface AccessibleAction methods ------------------------ public AccessibleAction getAccessibleAction() { return this; } /** * Returns the number of accessible actions available in this object * If there are more than one, the first one is considered the * "default" action of the object. * * @return the zero-based number of Actions in this object * @since 1.4 */ public int getAccessibleActionCount() { Action [] actions = JTextComponent.this.getActions(); return actions.length; } /** * Returns a description of the specified action of the object. * * @param i zero-based index of the actions * @return a String description of the action * @see #getAccessibleActionCount * @since 1.4 */ public String getAccessibleActionDescription(int i) { Action [] actions = JTextComponent.this.getActions(); if (i < 0 || i >= actions.length) { return null; } return (String)actions[i].getValue(Action.NAME); } /** * Performs the specified Action on the object * * @param i zero-based index of actions * @return true if the action was performed; otherwise false. * @see #getAccessibleActionCount * @since 1.4 */ public boolean doAccessibleAction(int i) { Action [] actions = JTextComponent.this.getActions(); if (i < 0 || i >= actions.length) { return false; } ActionEvent ae = new ActionEvent(JTextComponent.this, ActionEvent.ACTION_PERFORMED, null, EventQueue.getMostRecentEventTime(), getCurrentEventModifiers()); actions[i].actionPerformed(ae); return true; } // ----- end AccessibleAction methods } // --- serialization --------------------------------------------- private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); caretEvent = new MutableCaretEvent(this); addMouseListener(caretEvent); addFocusListener(caretEvent); } // --- member variables ---------------------------------- /** * The document model. */ private Document model; /** * The caret used to display the insert position * and navigate throughout the document. * * PENDING(prinz) * This should be serializable, default installed * by UI. */ private transient Caret caret; /** * Object responsible for restricting the cursor navigation. */ private NavigationFilter navigationFilter; /** * The object responsible for managing highlights. * * PENDING(prinz) * This should be serializable, default installed * by UI. */ private transient Highlighter highlighter; /** * The current key bindings in effect. * * PENDING(prinz) * This should be serializable, default installed * by UI. */ private transient Keymap keymap; private transient MutableCaretEvent caretEvent; private Color caretColor; private Color selectionColor; private Color selectedTextColor; private Color disabledTextColor; private boolean editable; private Insets margin; private char focusAccelerator; private boolean dragEnabled; /** * The drop mode for this component. */ private DropMode dropMode = DropMode.USE_SELECTION; /** * The drop location. */ private transient DropLocation dropLocation; /** * Represents a drop location for <code>JTextComponent</code>s. * * @see #getDropLocation * @since 1.6 */ public static final class DropLocation extends TransferHandler.DropLocation { private final int index; private final Position.Bias bias; private DropLocation(Point p, int index, Position.Bias bias) { super(p); this.index = index; this.bias = bias; } /** * Returns the index where dropped data should be inserted into the * associated component. This index represents a position between * characters, as would be interpreted by a caret. * * @return the drop index */ public int getIndex() { return index; } /** * Returns the bias for the drop index. * * @return the drop bias */ public Position.Bias getBias() { return bias; } /** * Returns a string representation of this drop location. * This method is intended to be used for debugging purposes, * and the content and format of the returned string may vary * between implementations. * * @return a string representation of this drop location */ public String toString() { return getClass().getName() + "[dropPoint=" + getDropPoint() + "," + "index=" + index + "," + "bias=" + bias + "]"; } } /** * TransferHandler used if one hasn't been supplied by the UI. */ private static DefaultTransferHandler defaultTransferHandler; /** * Maps from class name to Boolean indicating if * <code>processInputMethodEvent</code> has been overriden. */ private static Map<String, Boolean> overrideMap; /** * Returns a string representation of this <code>JTextComponent</code>. * This method is intended to be used only for debugging purposes, and the * content and format of the returned string may vary between * implementations. The returned string may be empty but may not * be <code>null</code>. * <P> * Overriding <code>paramString</code> to provide information about the * specific new aspects of the JFC components. * * @return a string representation of this <code>JTextComponent</code> */ protected String paramString() { String editableString = (editable ? "true" : "false"); String caretColorString = (caretColor != null ? caretColor.toString() : ""); String selectionColorString = (selectionColor != null ? selectionColor.toString() : ""); String selectedTextColorString = (selectedTextColor != null ? selectedTextColor.toString() : ""); String disabledTextColorString = (disabledTextColor != null ? disabledTextColor.toString() : ""); String marginString = (margin != null ? margin.toString() : ""); return super.paramString() + ",caretColor=" + caretColorString + ",disabledTextColor=" + disabledTextColorString + ",editable=" + editableString + ",margin=" + marginString + ",selectedTextColor=" + selectedTextColorString + ",selectionColor=" + selectionColorString; } /** * A Simple TransferHandler that exports the data as a String, and * imports the data from the String clipboard. This is only used * if the UI hasn't supplied one, which would only happen if someone * hasn't subclassed Basic. */ static class DefaultTransferHandler extends TransferHandler implements UIResource { public void exportToClipboard(JComponent comp, Clipboard clipboard, int action) throws IllegalStateException { if (comp instanceof JTextComponent) { JTextComponent text = (JTextComponent)comp; int p0 = text.getSelectionStart(); int p1 = text.getSelectionEnd(); if (p0 != p1) { try { Document doc = text.getDocument(); String srcData = doc.getText(p0, p1 - p0); StringSelection contents =new StringSelection(srcData); // this may throw an IllegalStateException, // but it will be caught and handled in the // action that invoked this method clipboard.setContents(contents, null); if (action == TransferHandler.MOVE) { doc.remove(p0, p1 - p0); } } catch (BadLocationException ble) {} } } } public boolean importData(JComponent comp, Transferable t) { if (comp instanceof JTextComponent) { DataFlavor flavor = getFlavor(t.getTransferDataFlavors()); if (flavor != null) { InputContext ic = comp.getInputContext(); if (ic != null) { ic.endComposition(); }