| /* |
| * 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 org.apache.pivot.wtk.Bounds; |
| import org.apache.pivot.wtk.Dimensions; |
| import org.apache.pivot.wtk.TextPane; |
| import org.apache.pivot.wtk.text.Element; |
| |
| /** |
| * Some of the classes in the text hierarchy are very similar in layout ie. they |
| * lay their children out vertically. This class groups that functionality. |
| */ |
| abstract class TextPaneSkinVerticalElementView extends TextPaneSkinElementView { |
| |
| public TextPaneSkinVerticalElementView(TextPaneSkin textPaneSkin, Element element) { |
| super(textPaneSkin, element); |
| } |
| |
| @Override |
| protected void childLayout(int breakWidth) { |
| // TODO At some point, we may want to optimize this method by deferring layout of |
| // non-visible views. If so, we should not recycle views but rather recreate them |
| // (as is done in ParagraphView). This way, we avoid thread contention over the |
| // existing views (e.g. trying to paint one while modifying its size/location, etc.). |
| // Any invalid node views are simply replaced (in the queued callback, when the |
| // thread has finished processing the new ones). This allows the definition of |
| // validate() to remain as-is. Of course, if we redefine NodeView to implement |
| // ConstrainedVisual, this may no longer be an issue. |
| // Note that, if anything happens to invalidate the existence of the new views before |
| // they are added to the document view, we need to make sure they are disposed (i.e. |
| // detached). |
| |
| int width = 0; |
| int height = 0; |
| |
| for (TextPaneSkinNodeView nodeView : this) { |
| nodeView.layout(breakWidth); |
| |
| nodeView.setLocation(0, height); |
| |
| width = Math.max(width, nodeView.getWidth()); |
| height += nodeView.getHeight(); |
| } |
| |
| setSize(width, height); |
| } |
| |
| @Override |
| public Dimensions getPreferredSize(int breakWidth) { |
| int width = 0; |
| int height = 0; |
| |
| for (TextPaneSkinNodeView nodeView : this) { |
| Dimensions childDimensions = nodeView.getPreferredSize(breakWidth); |
| |
| width = Math.max(width, childDimensions.width); |
| height += childDimensions.height; |
| } |
| |
| return new Dimensions(width, height); |
| } |
| |
| @Override |
| protected void setSkinLocation(int skinX, int skinY) { |
| super.setSkinLocation(skinX, skinY); |
| for (TextPaneSkinNodeView nodeView : this) { |
| nodeView.setSkinLocation(skinX, skinY + nodeView.getY()); |
| } |
| } |
| |
| @Override |
| public int getInsertionPoint(int x, int y) { |
| int offset = -1; |
| |
| for (int i = 0, n = getLength(); i < n; i++) { |
| TextPaneSkinNodeView nodeView = get(i); |
| Bounds nodeViewBounds = nodeView.getBounds(); |
| |
| if (y >= nodeViewBounds.y && y < nodeViewBounds.y + nodeViewBounds.height) { |
| offset = nodeView.getInsertionPoint(x - nodeView.getX(), y - nodeView.getY()) |
| + nodeView.getOffset(); |
| break; |
| } |
| } |
| |
| return offset; |
| } |
| |
| @Override |
| public int getNextInsertionPoint(int x, int from, TextPane.ScrollDirection direction) { |
| int offset = -1; |
| |
| if (getLength() > 0) { |
| if (from == -1) { |
| int i = (direction == TextPane.ScrollDirection.DOWN) ? 0 : getLength() - 1; |
| TextPaneSkinNodeView nodeView = get(i); |
| offset = nodeView.getNextInsertionPoint(x - nodeView.getX(), -1, direction); |
| |
| if (offset != -1) { |
| offset += nodeView.getOffset(); |
| } |
| } else { |
| // Find the node view that contains the offset |
| int n = getLength(); |
| int i = 0; |
| |
| while (i < n) { |
| TextPaneSkinNodeView nodeView = get(i); |
| int nodeViewOffset = nodeView.getOffset(); |
| int characterCount = nodeView.getCharacterCount(); |
| |
| if (from >= nodeViewOffset && from < nodeViewOffset + characterCount) { |
| break; |
| } |
| |
| i++; |
| } |
| |
| if (i < n) { |
| TextPaneSkinNodeView nodeView = get(i); |
| offset = nodeView.getNextInsertionPoint(x - nodeView.getX(), |
| from - nodeView.getOffset(), direction); |
| |
| if (offset == -1) { |
| // Move to the next or previous node view |
| if (direction == TextPane.ScrollDirection.DOWN) { |
| nodeView = (i < n - 1) ? get(i + 1) : null; |
| } else { |
| nodeView = (i > 0) ? get(i - 1) : null; |
| } |
| |
| if (nodeView != null) { |
| offset = nodeView.getNextInsertionPoint(x - nodeView.getX(), -1, |
| direction); |
| } |
| } |
| |
| if (offset != -1 && nodeView != null) { |
| offset += nodeView.getOffset(); |
| } |
| } |
| } |
| } |
| |
| return offset; |
| } |
| |
| @Override |
| public int getRowAt(int offset) { |
| int rowIndex = 0; |
| |
| for (TextPaneSkinNodeView nodeView : this) { |
| int nodeViewOffset = nodeView.getOffset(); |
| int characterCount = nodeView.getCharacterCount(); |
| |
| if (offset >= nodeViewOffset && offset < nodeViewOffset + characterCount) { |
| rowIndex += nodeView.getRowAt(offset - nodeView.getOffset()); |
| break; |
| } |
| |
| rowIndex += nodeView.getRowCount(); |
| } |
| |
| return rowIndex; |
| } |
| |
| @Override |
| public int getRowCount() { |
| int rowCount = 0; |
| |
| for (TextPaneSkinNodeView nodeView : this) { |
| rowCount += nodeView.getRowCount(); |
| } |
| |
| return rowCount; |
| } |
| } |