Package com.ibm.richtext.textpanel

Source Code of com.ibm.richtext.textpanel.ATextPanelImpl

/*
* (C) Copyright IBM Corp. 1998-2004.  All Rights Reserved.
*
* The program is provided "as is" without any warranty express or
* implied, including the warranty of non-infringement and the implied
* warranties of merchantibility and fitness for a particular purpose.
* IBM will not be liable for any damages suffered by you as a result
* of using the Program. In no event will IBM be liable for any
* special, indirect or consequential damages or lost profits even if
* IBM has been advised of the possibility of their occurrence. IBM
* will not be liable for any third party claims against you.
*/
package com.ibm.richtext.textpanel;

import java.awt.Adjustable;
import java.awt.Component;
import java.awt.datatransfer.Clipboard;

import com.ibm.richtext.textlayout.attributes.AttributeMap;

import com.ibm.richtext.styledtext.StyleModifier;
import com.ibm.richtext.styledtext.MConstText;
import com.ibm.richtext.styledtext.MText;
import com.ibm.richtext.styledtext.StyledText;
import com.ibm.richtext.textformat.TextOffset;

/**
* Implementation class for TextPanel and JTextPanel.
*/
final class ATextPanelImpl {
   
    static final String COPYRIGHT =
                "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
               
    private RunStrategy fRunStrategy = null;
    private TextComponent fTextComponent = null;
    private TextSelection fSelection = null;
    private TextEditBehavior fEditBehavior = null;
    private MText fText = null;

    private PanelEventBroadcaster fBroadcaster;
    private KeyRemap fRemap = KeyRemap.getIdentityRemap();

    // This is a little ugly.  TextPanel supports its modified
    // flag whether or not it is editable, or even selectable.
    // So if there's no command log to keep track of the flag
    // state then its done right here in TextPanel.  If the
    // panel is editable this flag is ignored.
    private boolean fModified = false;

    static final TextPanelSettings fgDefaultSettings = new TextPanelSettings();

    static TextPanelSettings getDefaultSettings() {

        return (TextPanelSettings) fgDefaultSettings.clone();
    }
   
    ATextPanelImpl(RunStrategy runStrategy,
                   TextPanelSettings settings,
                   MConstText initialText,
                   Clipboard clipboard,
                   MTextPanel client,
                   Adjustable horzSb,
                   Adjustable vertSb) {
                   
        fRunStrategy = runStrategy;
        fBroadcaster = new PanelEventBroadcaster(client);

        Scroller scroller = null;
        if (settings.getScrollable()) {
            scroller = new Scroller(horzSb, vertSb);
        }

        StyledTextClipboard textClipboard =
                            StyledTextClipboard.getClipboardFor(clipboard);

        fText = new StyledText();
        if (initialText != null) {
            fText.append(initialText);
        }

        fTextComponent = new TextComponent(fText,
                                           settings.getDefaultValues(),
                                           settings.getWraps(),
                                           TextComponent.WINDOW_WIDTH,
                                           TextComponent.DEFAULT_INSET,
                                           textClipboard,
                                           settings.getScrollable(),
                                           scroller,
                                           fBroadcaster);

        if (scroller != null) {
            scroller.setClient(fTextComponent);
        }
       
        // May have to wait until component has host to do this:
        if (settings.getSelectable()) {
            fSelection = new TextSelection(fTextComponent,
                                           fBroadcaster,
                                           fRunStrategy);
            fSelection.addToOwner(fTextComponent);
            if (settings.getEditable()) {
                fEditBehavior = new TextEditBehavior(
                            fTextComponent, fSelection, fBroadcaster, fRemap);
                fEditBehavior.addToOwner(fTextComponent);
            }
        }
    }
   
    FakeComponent getTextComponent() {
       
        return fTextComponent;
    }
   
    /**
     * Add the given TextPanelListener to the listeners which will
     * receive update notifications from this TextPanel.
     * @param listener the listener to add
     */
    public void addListener(TextPanelListener listener) {

        fBroadcaster.addListener(listener);
    }

    /**
     * Remove the given TextPanelListener from the listeners which will
     * receive update notifications from this TextPanel.
     * @param listener the listener to remove
     */
    public void removeListener(TextPanelListener listener) {

        fBroadcaster.removeListener(listener);
    }

    /**
     * You know what this does...
     */
    private static int pin(int value, int min, int max) {

        if (min > max) {
            throw new IllegalArgumentException("Invalid range");
        }

        if (value < min) {
            value = min;
        }
        else if (value > max) {
            value = max;
        }
        return value;
    }

//============
// Text Access
//============

    /**
     * Set the document to <tt>newText</tt>.  This operation
     * modifies the text in the TextPanel.  It does not modify or adopt
     * <tt>newText</tt>.  This method sets the selection an insertion point at
     * the end of the text.
     * @param newText the text which will replace the current text.
     */
    public void setText(MConstText newText) {

        replaceRange(newText, 0, getTextLength());
    }

    /**
     * Append the given text to the end of the document.  Equivalent to
     * <tt>insert(newText, getTextLength())</tt>.
     * @param newText the text to append to the document
     */
    public void append(MConstText newText) {

        int length = getTextLength();
        replaceRange(newText, length, length);
    }

    /**
     * Insert the given text into the document at the given position.
     * Equivalent to
     * <tt>replaceRange(newText, position, position)</tt>.
     * @param newText the text to insert into the document.
     * @param position the position in the document where the
     *     text will be inserted
     */
    public void insert(MConstText newText, int position) {

        replaceRange(newText, position, position);
    }

    /**
     * Replace the given range with <tt>newText</tt>.  After this
     * operation the selection range is an insertion point at the
     * end of the new text.
     * @param newText the text with which to replace the range
     * @param start the beginning of the range to replace
     * @param end the end of the range to replace
     */
    public void replaceRange(MConstText newText, int start, int end) {

        int length = getTextLength();

        start = pin(start, 0, length);
        end = pin(end, start, length);

        if (fSelection != null) {

            // If we're selectable, but not editable, we'll temporarily
            // make ourselves editable to change the text.  A little funny
            // but there's a lot of code for getting caret stuff right,
            // and this is not a common operation anyway.

            TextEditBehavior behavior;

            if (fEditBehavior == null) {
                behavior = new TextEditBehavior(fTextComponent, fSelection, fBroadcaster, fRemap);
                behavior.addToOwner(fTextComponent);
            }
            else {
                behavior = fEditBehavior;
            }

            TextOffset newSelection = new TextOffset(start + newText.length(),
                                                     TextOffset.AFTER_OFFSET);

            TextReplacement replacement = new TextReplacement(start, end,
                                                              newText,
                                                              newSelection,
                                                              newSelection);

            fTextComponent.textControlEventOccurred(Behavior.REPLACE,
                                                    replacement);
            if (fEditBehavior == null) {
                behavior.removeFromOwner();
            }
        }
        else {

            MText oldText = fTextComponent.getModifiableText();
            fTextComponent.stopBackgroundFormatting();
            oldText.replaceAll(newText);
            fTextComponent.reformatAndDrawText(0, newText.length(), null, null, null, null);
        }
    }

    /**
     * Return the length of the text document in the TextPanel.
     * @return the length of the text document in the TextPanel
     */
    public int getTextLength() {

        return fTextComponent.getText().length();
    }

    /**
     * Return the text document in the TextPanel.
     * @return the text document in the TextPanel.
     */
    public MConstText getText() {

        return fTextComponent.getText();
    }

//============
// Selection Access
//============

    /**
     * Return the offset of the start of the selection.
     */
    public int getSelectionStart() {

        if (fSelection != null) {
            return fSelection.getStart().fOffset;
        }
        else {
            return 0;
        }
    }

    /**
     * Return the offset of the end of the selection.
     */
    public int getSelectionEnd() {

        if (fSelection != null) {
            return fSelection.getEnd().fOffset;
        }
        else {
            return 0;
        }
    }

    /**
     * Set the beginning of the selection range.  This is
     * equivalent to <tt>select(selectionStart, getSelectionEnd())</tt>.
     * @param selectionStart the start of the new selection range
     */
    public void setSelectionStart(int selectionStart) {

        select(selectionStart, getSelectionEnd());
    }

    /**
     * Set the end of the selection range.  This is
     * equivalent to <tt>select(getSelectionStart(), selectionEnd)</tt>.
     * @param selectionStart the start of the new selection range
     */
    public void setSelectionEnd(int selectionEnd) {

        select(getSelectionStart(), selectionEnd);
    }

    /**
     * Set the selection range to an insertion point at the given
     * offset.  This is equivalent to
     * <tt>select(position, position)</tt>.
     * @param position the offset of the new insertion point
     */
    public void setCaretPosition(int position) {

        select(position, position);
    }

    /**
     * Set the selection range to the given range.  The range start
     * is pinned between 0 and the text length;  the range end is pinned
     * between the range start and the end of the text.  These semantics
     * are identical to those of <tt>java.awt.TextComponent</tt>.
     * This method has no effect if the text is not selectable.
     * @param selectionStart the beginning of the selection range
     * @param selectionEnd the end of the selection range
     */
    public void select(int selectionStart, int selectionEnd) {

        int length = getTextLength();

        selectionStart = pin(selectionStart, 0, length);
        selectionEnd = pin(selectionEnd, selectionStart, length);

        TextRange range = new TextRange(selectionStart, selectionEnd);
        fTextComponent.textControlEventOccurred(Behavior.SELECT, range);
    }

    /**
     * Select all of the text in the document.  This method has no effect if
     * the text is not selectable.
     */
    public void selectAll() {

        select(0, getTextLength());
    }


//============
// Format Width
//============

    /**
     * Return the total format width, in pixels.  The format width is the
     * width to which text is wrapped.
     * @return the format width
     */
    public int getFormatWidth() {

        return fTextComponent.getFormatWidth();
    }

    /**
     * Return true if the paragraph at the given offset is left-to-right.
     * @param offset an offset in the text
     * @return true if the paragraph at the given offset is left-to-right
     */
    public boolean paragraphIsLeftToRight(int offset) {
       
        return fTextComponent.paragraphIsLeftToRight(offset);
    }

    /**
     * Return true if there is a change which can be undone.
     * @return true if there is a change which can be undone.
     */
    public boolean canUndo() {

        if (fEditBehavior != null) {
            return fEditBehavior.canUndo();
        }
        else {
            return false;
        }
    }

    /**
     * Return true if there is a change which can be redone.
     * @return true if there is a change which can be redone.
     */
    public boolean canRedo() {

        if (fEditBehavior != null) {
            return fEditBehavior.canRedo();
        }
        else {
            return false;
        }
    }

    /**
     * Return true if the clipboard contains contents which could be
     * transfered into the text.
     * @return true if the clipboard has text content.
     */
    public boolean clipboardNotEmpty() {

        return fTextComponent.getClipboard().hasContents();
    }

    /**
     * Return an AttributeMap of keys with default values.  The default
     * values are used when displaying text for values which are not
     * specified in the text.
     * @return an AttributeMap of default key-value pairs
     */
    public AttributeMap getDefaultValues() {

        return fTextComponent.getDefaultValues();
    }
   
    private static boolean objectsAreEqual(Object lhs, Object rhs) {
   
        if (lhs == null) {
            return rhs == null;
        }
        else {
            return lhs.equals(rhs);
        }
    }

    private static Object consistentCharStyle(MConstText text,
                                              int start,
                                              int limit,
                                              Object key,
                                              Object defaultValue) {

        if (start >= limit) {
            throw new IllegalArgumentException("Invalid range.");
        }

        int runStart = start;
        Object initialValue = text.characterStyleAt(runStart).get(key);

        if (initialValue == null) {
            initialValue = defaultValue;
        }

        for (runStart = text.characterStyleLimit(runStart);
             runStart < limit;
             runStart = text.characterStyleLimit(runStart)) {

            Object nextValue = text.characterStyleAt(runStart).get(key);

            if (nextValue == null) {
                nextValue = defaultValue;
            }

            if (!objectsAreEqual(initialValue, nextValue)) {
                return MTextPanel.MULTIPLE_VALUES;
            }
        }

        return initialValue;
    }

    /**
     * This method inspects the character style runs in the selection
     * range (or the typing style at the insertion point) and returns:
     * <ul>
     * <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
     * is the same in all of the style runs in the selection, or</li>
     * <li>null, if two or more style runs have different values for <tt>key</tt>.</li>
     * </ul>
     * If a style run does not contain <tt>key</tt>,
     * its value is considered to be <tt>defaultStyle</tt>.
     * This method is useful for configuring style menus.
     * @param key the key used to retrieve values for comparison
     * @param defaultValue the implicit value of <tt>key</tt> in
     *     style runs where <tt>key</tt> is not defined
     */
    public Object getCharacterStyleOverSelection(Object key) {

        TextRange selRange;
        if (fSelection != null)
            selRange = fSelection.getSelectionRange();
        else
            selRange = new TextRange(0, 0);

        if (selRange.start == selRange.limit) {

            AttributeMap compStyle;

            if (fEditBehavior != null) {
                compStyle = fEditBehavior.getInsertionPointStyle();
            }
            else {
                compStyle = TextEditBehavior.typingStyleAt(fText, selRange.start, selRange.limit);
            }

            Object value = compStyle.get(key);
            return value==null? getDefaultValues().get(key) : value;
        }
        else {
            return consistentCharStyle(fText,
                                       selRange.start,
                                       selRange.limit,
                                       key,
                                       getDefaultValues().get(key));
        }
    }

    /**
     * This method inspects the paragraph style runs in the selection
     * range (or the typing style at the insertion point) and returns:
     * <ul>
     * <li>The value of <tt>key</tt>, if the value of <tt>key</tt>
     * is the same in all of the style runs in the selection, or</li>
     * <li>null, if two or more style runs have different values for <tt>key</tt>.</li>
     * </ul>
     * If a style run does not contain <tt>key</tt>,
     * its value is considered to be <tt>defaultStyle</tt>.
     * This method is useful for configuring style menus.
     * @param key the key used to retrieve values for comparison
     * @param defaultValue the implicit value of <tt>key</tt> in
     *     style runs where <tt>key</tt> is not defined
     */
    public Object getParagraphStyleOverSelection(Object key) {

        TextRange selRange;
        if (fSelection != null) {
            selRange = fSelection.getSelectionRange();
        }
        else {
            selRange = new TextRange(0, 0);
        }

        if (selRange.start == selRange.limit) {
            AttributeMap pStyle = fText.paragraphStyleAt(selRange.start);
            Object value = pStyle.get(key);
            return value==null? getDefaultValues().get(key) : value;
        }
        else {
            int paragraphStart = selRange.start;
            Object defaultValue = getDefaultValues().get(key);
            Object initialValue = fText.paragraphStyleAt(paragraphStart).get(key);
            if (initialValue == null) {
                initialValue = defaultValue;
            }

            for (paragraphStart = fText.paragraphLimit(paragraphStart);
                 paragraphStart < selRange.limit;
                 paragraphStart = fText.paragraphLimit(paragraphStart)) {

                Object nextValue = fText.paragraphStyleAt(paragraphStart).get(key);
                if (nextValue == null) {
                    nextValue = defaultValue;
                }

                if (!objectsAreEqual(initialValue, nextValue)) {
                    return MTextPanel.MULTIPLE_VALUES;
                }
            }

            return initialValue;
        }
    }

    /**
     * Remove the selected text from the document and place it
     * on the clipboard.  This method has no effect if the text
     * is not editable, or if no text is selected.
     */
    public void cut() {
        fTextComponent.textControlEventOccurred(Behavior.CUT, null);
    }

    /**
     * Place the selected text on the clipboard.  This method has
     * no effect if no text is selected.
     */
    public void copy() {
        fTextComponent.textControlEventOccurred(Behavior.COPY, null);
    }

    /**
     * Replace the currently selected text with the text on the clipboard.
     * This method has no effect if the text is not editable, or if no
     * text is on the clipboard.
     */
    public void paste() {
        fTextComponent.textControlEventOccurred(Behavior.PASTE, null);
    }

    /**
     * Remove selected text from the document, without altering the clipboard.
     * This method has no effect if the
     * text is not editable.
     */
    public void clear() {
        fTextComponent.textControlEventOccurred(Behavior.CLEAR, null);
    }

    /**
     * Undo the most recent text change.  This method has no effect if
     * there is no change to undo.
     */
    public void undo() {
        fTextComponent.textControlEventOccurred(Behavior.UNDO, null);
    }

    /**
     * Redo the most recent text change.  This method has no effect if
     * there is no change to redo.
     */
    public void redo() {
        fTextComponent.textControlEventOccurred(Behavior.REDO, null);
    }

    /**
     * Return the number of commands the command log can hold.
     * @return the number of commands the command log can hold
     */
    public int getCommandLogSize() {

        if (fEditBehavior != null) {
            return fEditBehavior.getCommandLogSize();
        }
        else {
            return 0;
        }
    }

    /**
     * Set the number of commands the command log can hold.  All
     * redoable commands are removed when this method is called.
     * @param size the number of commands kept in the command log
     */
    public void setCommandLogSize(int size) {
        fTextComponent.textControlEventOccurred(Behavior.SET_COMMAND_LOG_SIZE,
                                                new Integer(size));
    }

    /**
     * Remove all commands from the command log.
     */
    public void clearCommandLog() {
        fTextComponent.textControlEventOccurred(Behavior.CLEAR_COMMAND_LOG, null);
    }

    /**
     * Modify the character styles on the selected characters.  If no characters
     * are selected, modify the typing style.
     * @param modifier the StyleModifier with which to modify the styles
     */
    public void modifyCharacterStyleOnSelection(StyleModifier modifier) {
        fTextComponent.textControlEventOccurred(Behavior.CHARACTER_STYLE_MOD, modifier);
    }

    /**
     * Modify the paragraph styles in paragraphs containing selected characters, or
     * the paragraph containing the insertion point.
     * @param modifier the StyleModifier with which to modify the styles
     */
    public void modifyParagraphStyleOnSelection(StyleModifier modifier) {
        fTextComponent.textControlEventOccurred(Behavior.PARAGRAPH_STYLE_MOD, modifier);
    }

    /**
     * Return the KeyRemap used to process key events.
     * @return the key remap used to process key events
     * @see #setKeyRemap
     */
    public KeyRemap getKeyRemap() {

        return fRemap;
    }

    /**
     * Use the given KeyRemap to map key events to characters.
     * Only key
     * events are affected by the remap;  other text entering the
     * control (via the clipboard, for example) is not affected
     * by the KeyRemap.
     * <p>
     * Do not pass <tt>null</tt> to this method to leave key
     * events unmapped.  Instead, use <tt>KeyRemap.getIdentityRemap()</tt>
     * @param remap the KeyRemap to use for mapping key events to characters
     * @exception java.lang.NullPointerException if parameter is null
     * @see KeyRemap
     */
    public void setKeyRemap(KeyRemap remap) {

        if (remap == null) {
            throw new NullPointerException("remap can't be null");
        }

        fRemap = remap;
        if (fEditBehavior != null) {
            fEditBehavior.setKeyRemap(remap);
        }

        fBroadcaster.textStateChanged(TextPanelEvent.KEYREMAP_CHANGED);
    }

    /**
     * Return the modification flag of the current text change.
     * @see #setModified
     */
    public boolean isModified() {

        if (fEditBehavior != null) {
            return fEditBehavior.isModified();
        }
        else {
            return fModified;
        }
    }

    /**
     * Set the modification flag of the current text change.
     */
    public void setModified(boolean modified) {

        boolean handled = fTextComponent.textControlEventOccurred(
                                    Behavior.SET_MODIFIED,
                                    modified? Boolean.TRUE : Boolean.FALSE);
        if (!handled) {
            fModified = modified;
        }
    }

    /**
     * This method is for perf-testing only!
     */
    void handleKeyEvent(java.awt.event.KeyEvent keyEvent) {
   
        Component host = fTextComponent.getHost();
        if (host != null) {
            host.dispatchEvent(keyEvent);
        }
    }
}
TOP

Related Classes of com.ibm.richtext.textpanel.ATextPanelImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.