Merge from mainline
[official-gcc.git] / libjava / classpath / javax / swing / text / FlowView.java
blob6d4b9cd3174464ea5ea4880c29967c1e725f4df2
1 /* FlowView.java -- A composite View
2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.text;
41 import java.awt.Container;
42 import java.awt.Graphics;
43 import java.awt.Rectangle;
44 import java.awt.Shape;
45 import java.util.Iterator;
46 import java.util.Vector;
48 import javax.swing.SwingConstants;
49 import javax.swing.event.DocumentEvent;
51 /**
52 * A <code>View</code> that can flows it's children into it's layout space.
54 * The <code>FlowView</code> manages a set of logical views (that are
55 * the children of the {@link #layoutPool} field). These are translated
56 * at layout time into a set of physical views. These are the views that
57 * are managed as the real child views. Each of these child views represents
58 * a row and are laid out within a box using the superclasses behaviour.
59 * The concrete implementation of the rows must be provided by subclasses.
61 * @author Roman Kennke (roman@kennke.org)
63 public abstract class FlowView extends BoxView
65 /**
66 * A strategy for translating the logical views of a <code>FlowView</code>
67 * into the real views.
69 public static class FlowStrategy
71 /**
72 * Creates a new instance of <code>FlowStragegy</code>.
74 public FlowStrategy()
76 // Nothing to do here.
79 /**
80 * Receives notification from a <code>FlowView</code> that some content
81 * has been inserted into the document at a location that the
82 * <code>FlowView</code> is responsible for.
84 * The default implementation simply calls {@link #layout}.
86 * @param fv the flow view that sends the notification
87 * @param e the document event describing the change
88 * @param alloc the current allocation of the flow view
90 public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
92 layout(fv);
95 /**
96 * Receives notification from a <code>FlowView</code> that some content
97 * has been removed from the document at a location that the
98 * <code>FlowView</code> is responsible for.
100 * The default implementation simply calls {@link #layout}.
102 * @param fv the flow view that sends the notification
103 * @param e the document event describing the change
104 * @param alloc the current allocation of the flow view
106 public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
108 layout(fv);
112 * Receives notification from a <code>FlowView</code> that some attributes
113 * have changed in the document at a location that the
114 * <code>FlowView</code> is responsible for.
116 * The default implementation simply calls {@link #layout}.
118 * @param fv the flow view that sends the notification
119 * @param e the document event describing the change
120 * @param alloc the current allocation of the flow view
122 public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
124 layout(fv);
128 * Returns the logical view of the managed <code>FlowView</code>.
130 * @param fv the flow view for which to return the logical view
132 * @return the logical view of the managed <code>FlowView</code>
134 public View getLogicalView(FlowView fv)
136 return fv.layoutPool;
140 * Performs the layout for the whole view. By default this rebuilds
141 * all the physical views from the logical views of the managed FlowView.
143 * This is called by {@link FlowView#layout} to update the layout of
144 * the view.
146 * @param fv the flow view for which we perform the layout
148 public void layout(FlowView fv)
150 fv.removeAll();
151 Element el = fv.getElement();
153 int rowStart = el.getStartOffset();
154 int end = el.getEndOffset();
155 int rowIndex = 0;
156 while (rowStart >= 0 && rowStart < end)
158 View row = fv.createRow();
159 fv.append(row);
160 rowStart = layoutRow(fv, rowIndex, rowStart);
161 rowIndex++;
166 * Lays out one row of the flow view. This is called by {@link #layout}
167 * to fill one row with child views until the available span is exhausted.
169 * @param fv the flow view for which we perform the layout
170 * @param rowIndex the index of the row
171 * @param pos the start position for the row
173 * @return the start position of the next row
175 protected int layoutRow(FlowView fv, int rowIndex, int pos)
177 int spanLeft = fv.getFlowSpan(rowIndex);
178 if (spanLeft <= 0)
179 return -1;
181 int offset = pos;
182 View row = fv.getView(rowIndex);
183 int flowAxis = fv.getFlowAxis();
185 while (spanLeft > 0)
187 View child = createView(fv, offset, spanLeft, rowIndex);
188 if (child == null)
190 offset = -1;
191 break;
194 int span = (int) child.getPreferredSpan(flowAxis);
195 if (span > spanLeft)
197 offset = -1;
198 break;
201 row.append(child);
202 spanLeft -= span;
203 offset = child.getEndOffset();
205 return offset;
209 * Creates physical views that form the rows of the flow view. This
210 * can be an entire view from the logical view (if it fits within the
211 * available span), a fragment of such a view (if it doesn't fit in the
212 * available span and can be broken down) or <code>null</code> (if it does
213 * not fit in the available span and also cannot be broken down).
215 * @param fv the flow view
216 * @param offset the start offset for the view to be created
217 * @param spanLeft the available span
218 * @param rowIndex the index of the row
220 * @return a view to fill the row with, or <code>null</code> if there
221 * is no view or view fragment that fits in the available span
223 protected View createView(FlowView fv, int offset, int spanLeft,
224 int rowIndex)
226 // Find the logical element for the given offset.
227 View logicalView = getLogicalView(fv);
229 int viewIndex = logicalView.getViewIndex(offset, Position.Bias.Forward);
230 if (viewIndex == -1)
231 return null;
233 View child = logicalView.getView(viewIndex);
234 int flowAxis = fv.getFlowAxis();
235 int span = (int) child.getPreferredSpan(flowAxis);
237 if (span <= spanLeft)
238 return child;
239 else if (child.getBreakWeight(flowAxis, offset, spanLeft)
240 > BadBreakWeight)
241 // FIXME: What to do with the pos parameter here?
242 return child.breakView(flowAxis, offset, 0, spanLeft);
243 else
244 return null;
249 * This special subclass of <code>View</code> is used to represent
250 * the logical representation of this view. It does not support any
251 * visual representation, this is handled by the physical view implemented
252 * in the <code>FlowView</code>.
254 class LogicalView extends View
257 * The child views of this logical view.
259 Vector children;
262 * Creates a new LogicalView instance.
264 LogicalView(Element el)
266 super(el);
267 children = new Vector();
271 * Returns the container that holds this view. The logical view returns
272 * the enclosing FlowView's container here.
274 * @return the container that holds this view
276 public Container getContainer()
278 return FlowView.this.getContainer();
282 * Returns the number of child views of this logical view.
284 * @return the number of child views of this logical view
286 public int getViewCount()
288 return children.size();
292 * Returns the child view at the specified index.
294 * @param index the index
296 * @return the child view at the specified index
298 public View getView(int index)
300 return (View) children.get(index);
304 * Replaces some child views with other child views.
306 * @param offset the offset at which to replace child views
307 * @param length the number of children to remove
308 * @param views the views to be inserted
310 public void replace(int offset, int length, View[] views)
312 if (length > 0)
314 for (int count = 0; count < length; ++count)
315 children.remove(offset);
318 int endOffset = offset + views.length;
319 for (int i = offset; i < endOffset; ++i)
321 children.add(i, views[i - offset]);
322 // Set the parent of the child views to the flow view itself so
323 // it has something to resolve.
324 views[i - offset].setParent(FlowView.this);
329 * Returns the index of the child view that contains the specified
330 * position in the document model.
332 * @param pos the position for which we are searching the child view
333 * @param b the bias
335 * @return the index of the child view that contains the specified
336 * position in the document model
338 public int getViewIndex(int pos, Position.Bias b)
340 int index = -1;
341 int i = 0;
342 for (Iterator it = children.iterator(); it.hasNext(); i++)
344 View child = (View) it.next();
345 if (child.getStartOffset() >= pos
346 && child.getEndOffset() < pos)
348 index = i;
349 break;
352 return index;
356 * Throws an AssertionError because it must never be called. LogicalView
357 * only serves as a holder for child views and has no visual
358 * representation.
360 public float getPreferredSpan(int axis)
362 throw new AssertionError("This method must not be called in "
363 + "LogicalView.");
367 * Throws an AssertionError because it must never be called. LogicalView
368 * only serves as a holder for child views and has no visual
369 * representation.
371 public Shape modelToView(int pos, Shape a, Position.Bias b)
372 throws BadLocationException
374 throw new AssertionError("This method must not be called in "
375 + "LogicalView.");
379 * Throws an AssertionError because it must never be called. LogicalView
380 * only serves as a holder for child views and has no visual
381 * representation.
383 public void paint(Graphics g, Shape s)
385 throw new AssertionError("This method must not be called in "
386 + "LogicalView.");
390 * Throws an AssertionError because it must never be called. LogicalView
391 * only serves as a holder for child views and has no visual
392 * representation.
394 public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
396 throw new AssertionError("This method must not be called in "
397 + "LogicalView.");
402 * The shared instance of FlowStrategy.
404 static final FlowStrategy sharedStrategy = new FlowStrategy();
407 * The span of the <code>FlowView</code> that should be flowed.
409 protected int layoutSpan;
412 * Represents the logical child elements of this view, encapsulated within
413 * one parent view (an instance of a package private <code>LogicalView</code>
414 * class). These will be translated to a set of real views that are then
415 * displayed on screen. This translation is performed by the inner class
416 * {@link FlowStrategy}.
418 protected View layoutPool;
421 * The <code>FlowStrategy</code> to use for translating between the
422 * logical and physical view.
424 protected FlowStrategy strategy;
427 * Creates a new <code>FlowView</code> for the given
428 * <code>Element</code> and <code>axis</code>.
430 * @param element the element that is rendered by this FlowView
431 * @param axis the axis along which the view is tiled, either
432 * <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>, the flow
433 * axis is orthogonal to this one
435 public FlowView(Element element, int axis)
437 super(element, axis);
438 strategy = sharedStrategy;
442 * Returns the axis along which the view should be flowed. This is
443 * orthogonal to the axis along which the boxes are tiled.
445 * @return the axis along which the view should be flowed
447 public int getFlowAxis()
449 int axis = getAxis();
450 int flowAxis;
452 if (axis == X_AXIS)
453 flowAxis = Y_AXIS;
454 else
455 flowAxis = X_AXIS;
457 return flowAxis;
462 * Returns the span of the flow for the specified child view. A flow
463 * layout can be shaped by providing different span values for different
464 * child indices. The default implementation returns the entire available
465 * span inside the view.
467 * @param index the index of the child for which to return the span
469 * @return the span of the flow for the specified child view
471 public int getFlowSpan(int index)
473 return layoutSpan;
477 * Returns the location along the flow axis where the flow span starts
478 * given a child view index. The flow can be shaped by providing
479 * different values here.
481 * @param index the index of the child for which to return the flow location
483 * @return the location along the flow axis where the flow span starts
485 public int getFlowStart(int index)
487 return getLeftInset(); // TODO: Is this correct?
491 * Creates a new view that represents a row within a flow.
493 * @return a view for a new row
495 protected abstract View createRow();
498 * Loads the children of this view. The <code>FlowView</code> does not
499 * directly load its children. Instead it creates a logical view
500 * (@{link #layoutPool}) which is filled by the logical child views.
501 * The real children are created at layout time and each represent one
502 * row.
504 * This method is called by {@link View#setParent} in order to initialize
505 * the view.
507 * @param vf the view factory to use for creating the child views
509 protected void loadChildren(ViewFactory vf)
511 if (layoutPool == null)
513 layoutPool = new LogicalView(getElement());
515 Element el = getElement();
516 int count = el.getElementCount();
517 for (int i = 0; i < count; ++i)
519 Element childEl = el.getElement(i);
520 View childView = vf.create(childEl);
521 layoutPool.append(childView);
527 * Performs the layout of this view. If the span along the flow axis changed,
528 * this first calls {@link FlowStrategy#layout} in order to rebuild the
529 * rows of this view. Then the superclass's behaviour is called to arrange
530 * the rows within the box.
532 * @param width the width of the view
533 * @param height the height of the view
535 protected void layout(int width, int height)
537 boolean rebuild = false;
539 int flowAxis = getFlowAxis();
540 if (flowAxis == X_AXIS)
542 rebuild = !(width == layoutSpan);
543 layoutSpan = width;
545 else
547 rebuild = !(height == layoutSpan);
548 layoutSpan = height;
551 if (rebuild)
552 strategy.layout(this);
554 // TODO: If the span along the box axis has changed in the process of
555 // relayouting the rows (that is, if rows have been added or removed),
556 // call preferenceChanged in order to throw away cached layout information
557 // of the surrounding BoxView.
559 super.layout(width, height);
563 * Receice notification that some content has been inserted in the region
564 * that this view is responsible for. This calls
565 * {@link FlowStrategy#insertUpdate}.
567 * @param changes the document event describing the changes
568 * @param a the current allocation of the view
569 * @param vf the view factory that is used for creating new child views
571 public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
573 // First we must send the insertUpdate to the logical view so it can
574 // be updated accordingly.
575 layoutPool.insertUpdate(changes, a, vf);
576 strategy.insertUpdate(this, changes, getInsideAllocation(a));
580 * Receice notification that some content has been removed from the region
581 * that this view is responsible for. This calls
582 * {@link FlowStrategy#removeUpdate}.
584 * @param changes the document event describing the changes
585 * @param a the current allocation of the view
586 * @param vf the view factory that is used for creating new child views
588 public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
590 strategy.removeUpdate(this, changes, getInsideAllocation(a));
594 * Receice notification that some attributes changed in the region
595 * that this view is responsible for. This calls
596 * {@link FlowStrategy#changedUpdate}.
598 * @param changes the document event describing the changes
599 * @param a the current allocation of the view
600 * @param vf the view factory that is used for creating new child views
602 public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
604 strategy.changedUpdate(this, changes, getInsideAllocation(a));
608 * Returns the index of the child <code>View</code> for the given model
609 * position.
611 * This is implemented to iterate over the children of this
612 * view (the rows) and return the index of the first view that contains
613 * the given position.
615 * @param pos the model position for whicht the child <code>View</code> is
616 * queried
618 * @return the index of the child <code>View</code> for the given model
619 * position
621 protected int getViewIndexAtPosition(int pos)
623 // First make sure we have a valid layout.
624 if (!isAllocationValid())
625 layout(getWidth(), getHeight());
627 int count = getViewCount();
628 int result = -1;
630 for (int i = 0; i < count; ++i)
632 View child = getView(i);
633 int start = child.getStartOffset();
634 int end = child.getEndOffset();
635 if (start <= pos && end > pos)
637 result = i;
638 break;
641 return result;