Package org.apache.pivot.wtk.skin

Source Code of org.apache.pivot.wtk.skin.TextPaneSkinParagraphView$Row

/*
* 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.pivot.wtk.skin;

import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;

import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.wtk.Bounds;
import org.apache.pivot.wtk.HorizontalAlignment;
import org.apache.pivot.wtk.Platform;
import org.apache.pivot.wtk.TextPane;
import org.apache.pivot.wtk.text.Node;
import org.apache.pivot.wtk.text.Paragraph;

class TextPaneSkinParagraphView extends TextPaneSkinBlockView {

    private static final int PARAGRAPH_TERMINATOR_WIDTH = 4;

    private final TextPaneSkin textPaneSkin;

    private static class Row {
        public int x = 0;
        public int y = 0;
        public int width = 0;
        public int height = 0;
        public ArrayList<TextPaneSkinNodeView> nodeViews = new ArrayList<TextPaneSkinNodeView>();
    }

    private ArrayList<Row> rows = null;
    private Bounds terminatorBounds = new Bounds(0, 0, 0, 0);

    public TextPaneSkinParagraphView(TextPaneSkin textPaneSkin, Paragraph paragraph) {
        super(paragraph);
        this.textPaneSkin = textPaneSkin;
    }

    @Override
    public void invalidate() {
        super.invalidate();
        terminatorBounds = null;
    }

    @Override
    public void validate() {
        if (!isValid()) {
            // Remove and re-create the child views, because of line-breaking, a single TextNode
            // may be added as many TextPaneSkinTextNodeView's.

            // Break the views into multiple rows
            int breakWidth = getBreakWidth();

            Paragraph paragraph = (Paragraph)getNode();
            rows = new ArrayList<Row>();

            Row row = new Row();
            for (Node node : paragraph) {
                TextPaneSkinNodeView nodeView = textPaneSkin.createNodeView(node);

                nodeView.setBreakWidth(Math.max(breakWidth - (row.width
                    + PARAGRAPH_TERMINATOR_WIDTH), 0));
                nodeView.validate();

                int nodeViewWidth = nodeView.getWidth();

                if (row.width + nodeViewWidth > breakWidth
                    && row.width > 0) {
                    // The view is too big to fit in the remaining space,
                    // and it is not the only view in this row
                    rows.add(row);
                    row = new Row();
                    row.width = 0;
                }

                // Add the view to the row
                row.nodeViews.add(nodeView);
                row.width += nodeViewWidth;

                // If the view was split into multiple views, add them to
                // their own rows
                nodeView = nodeView.getNext();
                while (nodeView != null) {
                    rows.add(row);
                    row = new Row();

                    nodeView.setBreakWidth(breakWidth);
                    nodeView.validate();

                    row.nodeViews.add(nodeView);
                    row.width = nodeView.getWidth();

                    nodeView = nodeView.getNext();
                }
            }

            // Add the last row
            if (row.nodeViews.getLength() > 0) {
                rows.add(row);
            }

            // Clear all existing views
            remove(0, getLength());

            // Add the row views to this view, lay out, and calculate height
            int x = 0;
            int width = 0;
            int height = 0;
            for (int i = 0, n = rows.getLength(); i < n; i++) {
                row = rows.get(i);
                row.y = height;

                width = Math.max(width, row.width);

                // Determine the row height
                for (TextPaneSkinNodeView nodeView : row.nodeViews) {
                    row.height = Math.max(row.height, nodeView.getHeight());
                }

                if (paragraph.getHorizontalAlignment() == HorizontalAlignment.LEFT) {
                    x = 0;
                } else if (paragraph.getHorizontalAlignment() == HorizontalAlignment.CENTER) {
                    x = (width - row.width) / 2;
                } else {
                    // right alignment
                    x = width - row.width;
                }
                for (TextPaneSkinNodeView nodeView : row.nodeViews) {
                    // TODO Align to baseline
                    int y = row.height - nodeView.getHeight();

                    nodeView.setLocation(x, y + height);
                    x += nodeView.getWidth();

                    add(nodeView);
                }

                height += row.height;
            }

            // Recalculate terminator bounds
            FontRenderContext fontRenderContext = Platform.getFontRenderContext();
            LineMetrics lm = textPaneSkin.getFont().getLineMetrics("", 0, 0, fontRenderContext);
            int terminatorHeight = (int)Math.ceil(lm.getHeight());

            int terminatorY;
            if (getCharacterCount() == 1) {
                // The terminator is the only character in this paragraph
                terminatorY = 0;
            } else {
                terminatorY = height - terminatorHeight;
            }

            terminatorBounds = new Bounds(x, terminatorY,
                PARAGRAPH_TERMINATOR_WIDTH, terminatorHeight);

            // Ensure that the paragraph is visible even when empty
            width += terminatorBounds.width;
            height = Math.max(height, terminatorBounds.height);

            setSize(width, height);
        }

        super.validate();
    }

    @Override
    protected void setSkinLocation(int skinX, int skinY) {
        for (int i = 0, n = rows.getLength(); i < n; i++) {
            Row row = rows.get(i);
            for (TextPaneSkinNodeView nodeView : row.nodeViews) {
                nodeView.setSkinLocation(skinX + nodeView.getX(), skinY + nodeView.getY());
            }
        }
    }

    @Override
    public TextPaneSkinNodeView getNext() {
        return null;
    }

    @Override
    public int getInsertionPoint(int x, int y) {
        int offset = -1;

        int n = rows.getLength();
        if (n > 0) {
            for (int i = 0; i < n; i++) {
                Row row = rows.get(i);

                if (y >= row.y
                    && y < row.y + row.height) {
                    if (x < row.x) {
                        TextPaneSkinNodeView firstNodeView = row.nodeViews.get(0);
                        offset = firstNodeView.getOffset();
                    } else if (x > row.x + row.width - 1) {
                        TextPaneSkinNodeView lastNodeView = row.nodeViews.get(row.nodeViews.getLength() - 1);
                        offset = lastNodeView.getOffset() + lastNodeView.getCharacterCount();

                        if (offset < getCharacterCount() - 1) {
                            offset--;
                        }
                    } else {
                        for (TextPaneSkinNodeView nodeView : row.nodeViews) {
                            Bounds nodeViewBounds = nodeView.getBounds();

                            if (nodeViewBounds.contains(x, y)) {
                                offset = nodeView.getInsertionPoint(x - nodeView.getX(), y - nodeView.getY())
                                    + nodeView.getOffset();
                                break;
                            }
                        }
                    }
                }

                if (offset != -1) {
                    break;
                }
            }
        } else {
            offset = getCharacterCount() - 1;
        }

        return offset;
    }

    @Override
    public int getNextInsertionPoint(int x, int from, TextPane.ScrollDirection direction) {
        int offset = -1;

        int n = rows.getLength();
        if (n == 0
            && from == -1) {
            // There are no rows; select the terminator character
            offset = 0;
        } else {
            int i;
            if (from == -1) {
                i = (direction == TextPane.ScrollDirection.DOWN) ? -1 : rows.getLength();
            } else {
                // Find the row that contains offset
                if (from == getCharacterCount() - 1) {
                    i = rows.getLength() - 1;
                } else {
                    i = 0;
                    while (i < n) {
                        Row row = rows.get(i);
                        TextPaneSkinNodeView firstNodeView = row.nodeViews.get(0);
                        TextPaneSkinNodeView lastNodeView = row.nodeViews.get(row.nodeViews.getLength() - 1);
                        if (from >= firstNodeView.getOffset()
                            && from < lastNodeView.getOffset() + lastNodeView.getCharacterCount()) {
                            break;
                        }

                        i++;
                    }
                }
            }

            // Move to the next or previous row
            if (direction == TextPane.ScrollDirection.DOWN) {
                i++;
            } else {
                i--;
            }

            if (i >= 0
                && i < n) {
                // Find the node view that contains x and get the insertion point from it
                Row row = rows.get(i);

                for (TextPaneSkinNodeView nodeView : row.nodeViews) {
                    Bounds bounds = nodeView.getBounds();
                    if (x >= bounds.x
                        && x < bounds.x + bounds.width) {
                        offset = nodeView.getNextInsertionPoint(x - nodeView.getX(), -1, direction)
                            + nodeView.getOffset();
                        break;
                    }
                }

                if (offset == -1) {
                    // No node view contained the x position; move to the end of the row
                    TextPaneSkinNodeView lastNodeView = row.nodeViews.get(row.nodeViews.getLength() - 1);
                    offset = lastNodeView.getOffset() + lastNodeView.getCharacterCount();

                    if (offset < getCharacterCount() - 1) {
                        offset--;
                    }
                }
            }
        }

        return offset;
    }

    @Override
    public int getRowAt(int offset) {
        int rowIndex = -1;

        if (offset == getCharacterCount() - 1) {
            rowIndex = (rows.getLength() == 0) ? 0 : rows.getLength() - 1;
        } else {
            for (int i = 0, n = rows.getLength(); i < n; i++) {
                Row row = rows.get(i);
                TextPaneSkinNodeView firstNodeView = row.nodeViews.get(0);
                TextPaneSkinNodeView lastNodeView = row.nodeViews.get(row.nodeViews.getLength() - 1);

                if (offset >= firstNodeView.getOffset()
                    && offset < lastNodeView.getOffset() + lastNodeView.getCharacterCount()) {
                    rowIndex = i;
                    break;
                }
            }
        }

        return rowIndex;
    }

    @Override
    public int getRowCount() {
        return Math.max(rows.getLength(), 1);
    }

    @Override
    public Bounds getCharacterBounds(int offset) {
        Bounds bounds;

        if (offset == getCharacterCount() - 1) {
            bounds = terminatorBounds;
        } else {
            bounds = super.getCharacterBounds(offset);
        }

        return bounds;
    }

}
TOP

Related Classes of org.apache.pivot.wtk.skin.TextPaneSkinParagraphView$Row

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.