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)
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
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
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
;
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
66 * A strategy for translating the logical views of a <code>FlowView</code>
67 * into the real views.
69 public static class FlowStrategy
72 * Creates a new instance of <code>FlowStragegy</code>.
76 // Nothing to do here.
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
)
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
)
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
)
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
146 * @param fv the flow view for which we perform the layout
148 public void layout(FlowView fv
)
151 Element el
= fv
.getElement();
153 int rowStart
= el
.getStartOffset();
154 int end
= el
.getEndOffset();
156 while (rowStart
>= 0 && rowStart
< end
)
158 View row
= fv
.createRow();
160 rowStart
= layoutRow(fv
, rowIndex
, rowStart
);
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
);
182 View row
= fv
.getView(rowIndex
);
183 int flowAxis
= fv
.getFlowAxis();
187 View child
= createView(fv
, offset
, spanLeft
, rowIndex
);
194 int span
= (int) child
.getPreferredSpan(flowAxis
);
203 offset
= child
.getEndOffset();
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
,
226 // Find the logical element for the given offset.
227 View logicalView
= getLogicalView(fv
);
229 int viewIndex
= logicalView
.getViewIndex(offset
, Position
.Bias
.Forward
);
233 View child
= logicalView
.getView(viewIndex
);
234 int flowAxis
= fv
.getFlowAxis();
235 int span
= (int) child
.getPreferredSpan(flowAxis
);
237 if (span
<= spanLeft
)
239 else if (child
.getBreakWeight(flowAxis
, offset
, spanLeft
)
241 // FIXME: What to do with the pos parameter here?
242 return child
.breakView(flowAxis
, offset
, 0, spanLeft
);
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.
262 * Creates a new LogicalView instance.
264 LogicalView(Element 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
)
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
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
)
342 for (Iterator it
= children
.iterator(); it
.hasNext(); i
++)
344 View child
= (View
) it
.next();
345 if (child
.getStartOffset() >= pos
346 && child
.getEndOffset() < pos
)
356 * Throws an AssertionError because it must never be called. LogicalView
357 * only serves as a holder for child views and has no visual
360 public float getPreferredSpan(int axis
)
362 throw new AssertionError("This method must not be called in "
367 * Throws an AssertionError because it must never be called. LogicalView
368 * only serves as a holder for child views and has no visual
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 "
379 * Throws an AssertionError because it must never be called. LogicalView
380 * only serves as a holder for child views and has no visual
383 public void paint(Graphics g
, Shape s
)
385 throw new AssertionError("This method must not be called in "
390 * Throws an AssertionError because it must never be called. LogicalView
391 * only serves as a holder for child views and has no visual
394 public int viewToModel(float x
, float y
, Shape a
, Position
.Bias
[] b
)
396 throw new AssertionError("This method must not be called in "
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();
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
)
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
504 * This method is called by {@link View#setParent} in order to initialize
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
);
547 rebuild
= !(height
== layoutSpan
);
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
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
618 * @return the index of the child <code>View</code> for the given model
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();
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
)