/[classpath]/classpath/javax/swing/text/FlowView.java
ViewVC logotype

Diff of /classpath/javax/swing/text/FlowView.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1.2.1 by gnu_andrew, Tue Aug 2 20:12:38 2005 UTC revision 1.1.2.2 by gnu_andrew, Sat Sep 10 15:31:55 2005 UTC
# Line 38  exception statement from your version. * Line 38  exception statement from your version. *
38    
39  package javax.swing.text;  package javax.swing.text;
40    
41  // TODO: Implement this class.  import java.awt.Container;
42  public class FlowView  import java.awt.Graphics;
43    extends BoxView  import java.awt.Rectangle;
44    import java.awt.Shape;
45    import java.util.Vector;
46    
47    import javax.swing.event.DocumentEvent;
48    
49    /**
50     * A <code>View</code> that can flows it's children into it's layout space.
51     *
52     * The <code>FlowView</code> manages a set of logical views (that are
53     * the children of the {@link #layoutPool} field). These are translated
54     * at layout time into a set of physical views. These are the views that
55     * are managed as the real child views. Each of these child views represents
56     * a row and are laid out within a box using the superclasses behaviour.
57     * The concrete implementation of the rows must be provided by subclasses.
58     *
59     * @author Roman Kennke (roman@kennke.org)
60     */
61    public abstract class FlowView extends BoxView
62  {  {
63      /**
64       * A strategy for translating the logical views of a <code>FlowView</code>
65       * into the real views.
66       */
67      public static class FlowStrategy
68      {
69        /**
70         * Creates a new instance of <code>FlowStragegy</code>.
71         */
72        public FlowStrategy()
73        {
74        }
75    
76        /**
77         * Receives notification from a <code>FlowView</code> that some content
78         * has been inserted into the document at a location that the
79         * <code>FlowView</code> is responsible for.
80         *
81         * The default implementation simply calls {@link #layout}.
82         *
83         * @param fv the flow view that sends the notification
84         * @param e the document event describing the change
85         * @param alloc the current allocation of the flow view
86         */
87        public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
88        {
89          layout(fv);
90        }
91    
92        /**
93         * Receives notification from a <code>FlowView</code> that some content
94         * has been removed from the document at a location that the
95         * <code>FlowView</code> is responsible for.
96         *
97         * The default implementation simply calls {@link #layout}.
98         *
99         * @param fv the flow view that sends the notification
100         * @param e the document event describing the change
101         * @param alloc the current allocation of the flow view
102         */
103        public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
104        {
105          layout(fv);
106        }
107    
108        /**
109         * Receives notification from a <code>FlowView</code> that some attributes
110         * have changed in the document at a location that the
111         * <code>FlowView</code> is responsible for.
112         *
113         * The default implementation simply calls {@link #layout}.
114         *
115         * @param fv the flow view that sends the notification
116         * @param e the document event describing the change
117         * @param alloc the current allocation of the flow view
118         */
119        public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
120        {
121          layout(fv);
122        }
123    
124        /**
125         * Returns the logical view of the managed <code>FlowView</code>.
126         *
127         * @param fv the flow view for which to return the logical view
128         *
129         * @return the logical view of the managed <code>FlowView</code>
130         */
131        public View getLogicalView(FlowView fv)
132        {
133          return fv.layoutPool;
134        }
135    
136        /**
137         * Performs the layout for the whole view. By default this rebuilds
138         * all the physical views from the logical views of the managed FlowView.
139         *
140         * This is called by {@link FlowLayout#layout} to update the layout of
141         * the view.
142         *
143         * @param fv the flow view for which we perform the layout
144         */
145        public void layout(FlowView fv)
146        {
147          fv.removeAll();
148          Element el = fv.getElement();
149    
150          int rowStart = el.getStartOffset();
151          int end = el.getEndOffset();
152          int rowIndex = 0;
153          while (rowStart >= 0 && rowStart < end)
154            {
155              View row = fv.createRow();
156              fv.append(row);
157              rowStart = layoutRow(fv, rowIndex, rowStart);
158              rowIndex++;
159            }
160        }
161    
162        /**
163         * Lays out one row of the flow view. This is called by {@link #layout}
164         * to fill one row with child views until the available span is exhausted.
165         *
166         * @param fv the flow view for which we perform the layout
167         * @param rowIndex the index of the row
168         * @param pos the start position for the row
169         *
170         * @return the start position of the next row
171         */
172        protected int layoutRow(FlowView fv, int rowIndex, int pos)
173        {
174          int spanLeft = fv.getFlowSpan(rowIndex);
175          if (spanLeft <= 0)
176            return -1;
177    
178          int offset = pos;
179          View row = fv.getView(rowIndex);
180          int flowAxis = fv.getFlowAxis();
181    
182          while (spanLeft > 0)
183            {
184              View child = createView(fv, offset, spanLeft, rowIndex);
185              if (child == null)
186                break;
187    
188              int span = (int) child.getPreferredSpan(flowAxis);
189              if (span > spanLeft)
190                break;
191    
192              row.append(child);
193              spanLeft -= span;
194              offset = child.getEndOffset();
195            }
196          return offset;
197        }
198    
199        /**
200         * Creates physical views that form the rows of the flow view. This
201         * can be an entire view from the logical view (if it fits within the
202         * available span), a fragment of such a view (if it doesn't fit in the
203         * available span and can be broken down) or <code>null</code> (if it does
204         * not fit in the available span and also cannot be broken down).
205         *
206         * @param fv the flow view
207         * @param startOffset the start offset for the view to be created
208         * @param spanLeft the available span
209         * @param rowIndex the index of the row
210         *
211         * @return a view to fill the row with, or <code>null</code> if there
212         *         is no view or view fragment that fits in the available span
213         */
214        protected View createView(FlowView fv, int offset, int spanLeft,
215                                  int rowIndex)
216        {
217          // Find the logical element for the given offset.
218          View logicalView = getLogicalView(fv);
219    
220          int viewIndex = logicalView.getViewIndex(offset, Position.Bias.Forward);
221          View child = logicalView.getView(viewIndex);
222          int flowAxis = fv.getFlowAxis();
223          int span = (int) child.getPreferredSpan(flowAxis);
224    
225          if (span <= spanLeft)
226            return child;
227    
228          else if (child.getBreakWeight(flowAxis, offset, spanLeft)
229                   > BadBreakWeight)
230            // FIXME: What to do with the pos parameter here?
231            return child.breakView(flowAxis, offset, 0, spanLeft);
232          else
233            return null;
234        }
235      }
236    
237      /**
238       * This special subclass of <code>View</code> is used to represent
239       * the logical representation of this view. It does not support any
240       * visual representation, this is handled by the physical view implemented
241       * in the <code>FlowView</code>.
242       */
243      class LogicalView extends View
244      {
245        /**
246         * The child views of this logical view.
247         */
248        Vector children;
249    
250        /**
251         * Creates a new LogicalView instance.
252         */
253        LogicalView(Element el)
254        {
255          super(el);
256          children = new Vector();
257        }
258    
259        /**
260         * Returns the container that holds this view. The logical view returns
261         * the enclosing FlowView's container here.
262         *
263         * @return the container that holds this view
264         */
265        public Container getContainer()
266        {
267          return FlowView.this.getContainer();
268        }
269    
270        /**
271         * Returns the number of child views of this logical view.
272         *
273         * @return the number of child views of this logical view
274         */
275        public int getViewCount()
276        {
277          return children.size();
278        }
279    
280        /**
281         * Returns the child view at the specified index.
282         *
283         * @param index the index
284         *
285         * @return the child view at the specified index
286         */
287        public View getView(int index)
288        {
289          return (View) children.get(index);
290        }
291    
292        /**
293         * Replaces some child views with other child views.
294         *
295         * @param offset the offset at which to replace child views
296         * @param length the number of children to remove
297         * @param views the views to be inserted
298         */
299        public void replace(int offset, int length, View[] views)
300        {
301          if (length > 0)
302            {
303              for (int count = 0; count < length; ++count)
304                children.remove(offset);
305            }
306    
307          int endOffset = offset + views.length;
308          for (int i = offset; i < endOffset; ++i)
309            {
310              children.add(i, views[i - offset]);
311              // Set the parent of the child views to the flow view itself so
312              // it has something to resolve.
313              views[i - offset].setParent(FlowView.this);
314            }
315        }
316    
317        /**
318         * Returns the index of the child view that contains the specified
319         * position in the document model.
320         *
321         * @param pos the position for which we are searching the child view
322         * @param b the bias
323         *
324         * @return the index of the child view that contains the specified
325         *         position in the document model
326         */
327        public int getViewIndex(int pos, Position.Bias b)
328        {
329          return getElement().getElementIndex(pos);
330        }
331    
332        /**
333         * Throws an AssertionError because it must never be called. LogicalView
334         * only serves as a holder for child views and has no visual
335         * representation.
336         */
337        public float getPreferredSpan(int axis)
338        {
339          throw new AssertionError("This method must not be called in "
340                                   + "LogicalView.");
341        }
342    
343        /**
344         * Throws an AssertionError because it must never be called. LogicalView
345         * only serves as a holder for child views and has no visual
346         * representation.
347         */
348        public Shape modelToView(int pos, Shape a, Position.Bias b)
349          throws BadLocationException
350        {
351          throw new AssertionError("This method must not be called in "
352                                   + "LogicalView.");
353        }
354    
355        /**
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.
359         */
360        public void paint(Graphics g, Shape s)
361        {
362          throw new AssertionError("This method must not be called in "
363                                   + "LogicalView.");
364        }
365    
366        /**
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.
370         */
371        public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
372        {
373          throw new AssertionError("This method must not be called in "
374                                   + "LogicalView.");
375        }
376      }
377    
378      /**
379       * The shared instance of FlowStrategy.
380       */
381      static final FlowStrategy sharedStrategy = new FlowStrategy();
382    
383      /**
384       * The span of the <code>FlowView</code> that should be flowed.
385       */
386      protected int layoutSpan;
387    
388      /**
389       * Represents the logical child elements of this view, encapsulated within
390       * one parent view (an instance of a package private <code>LogicalView</code>
391       * class). These will be translated to a set of real views that are then
392       * displayed on screen. This translation is performed by the inner class
393       * {@link FlowStrategy}.
394       */
395      protected View layoutPool;
396    
397      /**
398       * The <code>FlowStrategy</code> to use for translating between the
399       * logical and physical view.
400       */
401      protected FlowStrategy strategy;
402    
403    /**    /**
404     * Creates a new <code>FlowView</code> for the given     * Creates a new <code>FlowView</code> for the given
405     * <code>Element</code>.     * <code>Element</code> and <code>axis</code>.
406     *     *
407     * @param element the element that is rendered by this FlowView     * @param element the element that is rendered by this FlowView
408       * @param axis the axis along which the view is tiled, either
409       *        <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>, the flow
410       *        axis is orthogonal to this one
411     */     */
412    public FlowView(Element element)    public FlowView(Element element, int axis)
413    {    {
414      super(element);      super(element, axis);
415        strategy = sharedStrategy;
416      }
417    
418      /**
419       * Returns the axis along which the view should be flowed. This is
420       * orthogonal to the axis along which the boxes are tiled.
421       *
422       * @return the axis along which the view should be flowed
423       */
424      public int getFlowAxis()
425      {
426        int axis = getAxis();
427        int flowAxis;
428    
429        if (axis == X_AXIS)
430          flowAxis = Y_AXIS;
431        else
432          flowAxis = X_AXIS;
433    
434        return flowAxis;
435    
436      }
437    
438      /**
439       * Returns the span of the flow for the specified child view. A flow
440       * layout can be shaped by providing different span values for different
441       * child indices. The default implementation returns the entire available
442       * span inside the view.
443       *
444       * @param index the index of the child for which to return the span
445       *
446       * @return the span of the flow for the specified child view
447       */
448      public int getFlowSpan(int index)
449      {
450        return layoutSpan;
451      }
452    
453      /**
454       * Returns the location along the flow axis where the flow span starts
455       * given a child view index. The flow can be shaped by providing
456       * different values here.
457       *
458       * @param index the index of the child for which to return the flow location
459       *
460       * @return the location along the flow axis where the flow span starts
461       */
462      public int getFlowStart(int index)
463      {
464        return getLeftInset(); // TODO: Is this correct?
465      }
466    
467      /**
468       * Creates a new view that represents a row within a flow.
469       *
470       * @return a view for a new row
471       */
472      protected abstract View createRow();
473    
474      /**
475       * Loads the children of this view. The <code>FlowView</code> does not
476       * directly load its children. Instead it creates a logical view
477       * (@{link #layoutPool}) which is filled by the logical child views.
478       * The real children are created at layout time and each represent one
479       * row.
480       *
481       * This method is called by {@link #setParent} in order to initialize
482       * the view.
483       *
484       * @param vf the view factory to use for creating the child views
485       */
486      protected void loadChildren(ViewFactory vf)
487      {
488        if (layoutPool == null)
489          {
490            layoutPool = new LogicalView(getElement());
491    
492            Element el = getElement();
493            int count = el.getElementCount();
494            for (int i = 0; i < count; ++i)
495              {
496                Element childEl = el.getElement(i);
497                View childView = vf.create(childEl);
498                layoutPool.append(childView);
499              }
500          }
501      }
502    
503      /**
504       * Performs the layout of this view. If the span along the flow axis changed,
505       * this first calls {@link FlowStrategy.layout} in order to rebuild the
506       * rows of this view. Then the superclass's behaviour is called to arrange
507       * the rows within the box.
508       *
509       * @param width the width of the view
510       * @param height the height of the view
511       */
512      protected void layout(int width, int height)
513      {
514        boolean rebuild = false;
515    
516        int flowAxis = getFlowAxis();
517        if (flowAxis == X_AXIS)
518          {
519            rebuild = !(width == layoutSpan);
520            layoutSpan = width;
521          }
522        else
523          {
524            rebuild = !(height == layoutSpan);
525            layoutSpan = height;
526          }
527    
528        if (rebuild)
529          strategy.layout(this);
530    
531        // TODO: If the span along the box axis has changed in the process of
532        // relayouting the rows (that is, if rows have been added or removed),
533        // call preferenceChanged in order to throw away cached layout information
534        // of the surrounding BoxView.
535    
536        super.layout(width, height);
537      }
538    
539      /**
540       * Receice notification that some content has been inserted in the region
541       * that this view is responsible for. This calls
542       * {@link FlowStrategy#insertUpdate}.
543       *
544       * @param changes the document event describing the changes
545       * @param a the current allocation of the view
546       * @param vf the view factory that is used for creating new child views
547       */
548      public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
549      {
550        strategy.insertUpdate(this, changes, getInsideAllocation(a));
551      }
552    
553      /**
554       * Receice notification that some content has been removed from the region
555       * that this view is responsible for. This calls
556       * {@link FlowStrategy#removeUpdate}.
557       *
558       * @param changes the document event describing the changes
559       * @param a the current allocation of the view
560       * @param vf the view factory that is used for creating new child views
561       */
562      public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
563      {
564        strategy.removeUpdate(this, changes, getInsideAllocation(a));
565      }
566    
567      /**
568       * Receice notification that some attributes changed in the region
569       * that this view is responsible for. This calls
570       * {@link FlowStrategy#changedUpdate}.
571       *
572       * @param changes the document event describing the changes
573       * @param a the current allocation of the view
574       * @param vf the view factory that is used for creating new child views
575       */
576      public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
577      {
578        strategy.changedUpdate(this, changes, getInsideAllocation(a));
579      }
580    
581      /**
582       * Returns the index of the child <code>View</code> for the given model
583       * position.
584       *
585       * This is implemented to iterate over the children of this
586       * view (the rows) and return the index of the first view that contains
587       * the given position.
588       *
589       * @param pos the model position for whicht the child <code>View</code> is
590       *        queried
591       *
592       * @return the index of the child <code>View</code> for the given model
593       *         position
594       */
595      protected int getViewIndexAtPosition(int pos)
596      {
597        // First make sure we have a valid layout.
598        if (!isAllocationValid())
599          layout(getWidth(), getHeight());
600    
601        int count = getViewCount();
602        int result = -1;
603    
604        for (int i = 0; i < count; ++i)
605          {
606            View child = getView(i);
607            int start = child.getStartOffset();
608            int end = child.getEndOffset();
609            if (start <= pos && end > pos)
610              {
611                result = i;
612                break;
613              }
614          }
615        return result;
616    }    }
617  }  }

Legend:
Removed from v.1.1.2.1  
changed lines
  Added in v.1.1.2.2

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26