OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [javax/] [swing/] [text/] [AsyncBoxView.java] - Blame information for rev 772

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 772 jeremybenn
/* AsyncBoxView.java -- A box view that performs layout asynchronously
2
   Copyright (C) 2006 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
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.
10
 
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.
15
 
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.
20
 
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.
25
 
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. */
37
 
38
 
39
package javax.swing.text;
40
 
41
import java.awt.Component;
42
import java.awt.Graphics;
43
import java.awt.Rectangle;
44
import java.awt.Shape;
45
import java.util.ArrayList;
46
 
47
import javax.swing.event.DocumentEvent;
48
import javax.swing.text.Position.Bias;
49
 
50
/**
51
 * A {@link View} implementation that lays out its child views in a box, either
52
 * vertically or horizontally. The difference to {@link BoxView} is that the
53
 * layout is performed in an asynchronous manner. This helps to keep the
54
 * eventqueue free from non-GUI related tasks.
55
 *
56
 * This view is currently not used in standard text components. In order to
57
 * use it you would have to implement a special {@link EditorKit} with a
58
 * {@link ViewFactory} that returns this view. For example:
59
 *
60
 * <pre>
61
 * static class AsyncEditorKit extends StyledEditorKit implements ViewFactory
62
 * {
63
 *   public View create(Element el)
64
 *   {
65
 *     if (el.getName().equals(AbstractDocument.SectionElementName))
66
 *       return new AsyncBoxView(el, View.Y_AXIS);
67
 *     return super.getViewFactory().create(el);
68
 *   }
69
 *   public ViewFactory getViewFactory() {
70
 *     return this;
71
 *   }
72
 * }
73
 * </pre>
74
 *
75
 * @author Roman Kennke (kennke@aicas.com)
76
 *
77
 * @since 1.3
78
 */
79
public class AsyncBoxView
80
  extends View
81
{
82
 
83
  /**
84
   * Manages the effective position of child views. That keeps the visible
85
   * layout stable while the AsyncBoxView might be changing until the layout
86
   * thread decides to publish the new layout.
87
   */
88
  public class ChildLocator
89
  {
90
 
91
    /**
92
     * The last valid location.
93
     */
94
    protected ChildState lastValidOffset;
95
 
96
    /**
97
     * The last allocation.
98
     */
99
    protected Rectangle lastAlloc;
100
 
101
    /**
102
     * A Rectangle used for child allocation calculation to avoid creation
103
     * of lots of garbage Rectangle objects.
104
     */
105
    protected Rectangle childAlloc;
106
 
107
    /**
108
     * Creates a new ChildLocator.
109
     */
110
    public ChildLocator()
111
    {
112
      lastAlloc = new Rectangle();
113
      childAlloc = new Rectangle();
114
    }
115
 
116
    /**
117
     * Receives notification that a child has changed. This is called by
118
     * child state objects that have changed it's major span.
119
     *
120
     * This sets the {@link #lastValidOffset} field to <code>cs</code> if
121
     * the new child state's view start offset is smaller than the start offset
122
     * of the current child state's view or when <code>lastValidOffset</code>
123
     * is <code>null</code>.
124
     *
125
     * @param cs the child state object that has changed
126
     */
127
    public synchronized void childChanged(ChildState cs)
128
    {
129
      if (lastValidOffset == null
130
          || cs.getChildView().getStartOffset()
131
             < lastValidOffset.getChildView().getStartOffset())
132
        {
133
          lastValidOffset = cs;
134
        }
135
    }
136
 
137
    /**
138
     * Returns the view index of the view that occupies the specified area, or
139
     * <code>-1</code> if there is no such child view.
140
     *
141
     * @param x the x coordinate (relative to <code>a</code>)
142
     * @param y the y coordinate (relative to <code>a</code>)
143
     * @param a the current allocation of this view
144
     *
145
     * @return the view index of the view that occupies the specified area, or
146
     *         <code>-1</code> if there is no such child view
147
     */
148
    public int getViewIndexAtPoint(float x, float y, Shape a)
149
    {
150
      setAllocation(a);
151
      float targetOffset = (getMajorAxis() == X_AXIS) ? x - lastAlloc.x
152
                                                      : y - lastAlloc.y;
153
      int index = getViewIndexAtVisualOffset(targetOffset);
154
      return index;
155
    }
156
 
157
    /**
158
     * Returns the current allocation for a child view. This updates the
159
     * offsets for all children <em>before</em> the requested child view.
160
     *
161
     * @param index the index of the child view
162
     * @param a the current allocation of this view
163
     *
164
     * @return the current allocation for a child view
165
     */
166
    public synchronized Shape getChildAllocation(int index, Shape a)
167
    {
168
      if (a == null)
169
        return null;
170
      setAllocation(a);
171
      ChildState cs = getChildState(index);
172
      if (cs.getChildView().getStartOffset()
173
          > lastValidOffset.getChildView().getStartOffset())
174
        {
175
          updateChildOffsetsToIndex(index);
176
        }
177
      Shape ca = getChildAllocation(index);
178
      return ca;
179
    }
180
 
181
    /**
182
     * Paints all child views.
183
     *
184
     * @param g the graphics context to use
185
     */
186
    public synchronized void paintChildren(Graphics g)
187
    {
188
      Rectangle clip = g.getClipBounds();
189
      float targetOffset = (getMajorAxis() == X_AXIS) ? clip.x - lastAlloc.x
190
                                                      : clip.y - lastAlloc.y;
191
      int index = getViewIndexAtVisualOffset(targetOffset);
192
      int n = getViewCount();
193
      float offs = getChildState(index).getMajorOffset();
194
      for (int i = index; i < n; i++)
195
        {
196
          ChildState cs = getChildState(i);
197
          cs.setMajorOffset(offs);
198
          Shape ca = getChildAllocation(i);
199
          if (ca.intersects(clip))
200
            {
201
              synchronized (cs)
202
                {
203
                  View v = cs.getChildView();
204
                  v.paint(g, ca);
205
                }
206
            }
207
          else
208
            {
209
              // done painting intersection
210
              break;
211
            }
212
          offs += cs.getMajorSpan();
213
        }
214
    }
215
 
216
    /**
217
     * Returns the current allocation of the child view with the specified
218
     * index. Note that this will <em>not</em> update any location information.
219
     *
220
     * @param index the index of the requested child view
221
     *
222
     * @return the current allocation of the child view with the specified
223
     *         index
224
     */
225
    protected Shape getChildAllocation(int index)
226
    {
227
      ChildState cs = getChildState(index);
228
      if (! cs.isLayoutValid())
229
          cs.run();
230
 
231
      if (getMajorAxis() == X_AXIS)
232
        {
233
          childAlloc.x = lastAlloc.x + (int) cs.getMajorOffset();
234
          childAlloc.y = lastAlloc.y + (int) cs.getMinorOffset();
235
          childAlloc.width = (int) cs.getMajorSpan();
236
          childAlloc.height = (int) cs.getMinorSpan();
237
        }
238
      else
239
        {
240
          childAlloc.y = lastAlloc.y + (int) cs.getMajorOffset();
241
          childAlloc.x = lastAlloc.x + (int) cs.getMinorOffset();
242
          childAlloc.height = (int) cs.getMajorSpan();
243
          childAlloc.width = (int) cs.getMinorSpan();
244
        }
245
      return childAlloc;
246
    }
247
 
248
    /**
249
     * Sets the current allocation for this view.
250
     *
251
     * @param a the allocation to set
252
     */
253
    protected void setAllocation(Shape a)
254
    {
255
      if (a instanceof Rectangle)
256
        lastAlloc.setBounds((Rectangle) a);
257
      else
258
        lastAlloc.setBounds(a.getBounds());
259
 
260
      setSize(lastAlloc.width, lastAlloc.height);
261
    }
262
 
263
    /**
264
     * Returns the index of the view at the specified offset along the major
265
     * layout axis.
266
     *
267
     * @param targetOffset the requested offset
268
     *
269
     * @return the index of the view at the specified offset along the major
270
     * layout axis
271
     */
272
    protected int getViewIndexAtVisualOffset(float targetOffset)
273
    {
274
      int n = getViewCount();
275
      if (n > 0)
276
        {
277
          if (lastValidOffset == null)
278
            lastValidOffset = getChildState(0);
279
          if (targetOffset > majorSpan)
280
            return 0;
281
          else if (targetOffset > lastValidOffset.getMajorOffset())
282
            return updateChildOffsets(targetOffset);
283
          else
284
            {
285
              float offs = 0f;
286
              for (int i = 0; i < n; i++)
287
                {
288
                  ChildState cs = getChildState(i);
289
                  float nextOffs = offs + cs.getMajorSpan();
290
                  if (targetOffset < nextOffs)
291
                    return i;
292
                  offs = nextOffs;
293
                }
294
            }
295
        }
296
      return n - 1;
297
    }
298
 
299
    /**
300
     * Updates all the child view offsets up to the specified targetOffset.
301
     *
302
     * @param targetOffset the offset up to which the child view offsets are
303
     *        updated
304
     *
305
     * @return the index of the view at the specified offset
306
     */
307
    private int updateChildOffsets(float targetOffset)
308
    {
309
      int n = getViewCount();
310
      int targetIndex = n - 1;
311
      int pos = lastValidOffset.getChildView().getStartOffset();
312
      int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
313
      float start = lastValidOffset.getMajorOffset();
314
      float lastOffset = start;
315
      for (int i = startIndex; i < n; i++)
316
        {
317
          ChildState cs = getChildState(i);
318
          cs.setMajorOffset(lastOffset);
319
          lastOffset += cs.getMajorSpan();
320
          if (targetOffset < lastOffset)
321
            {
322
              targetIndex = i;
323
              lastValidOffset = cs;
324
              break;
325
            }
326
        }
327
      return targetIndex;
328
    }
329
 
330
    /**
331
     * Updates the offsets of the child views up to the specified index.
332
     *
333
     * @param index the index up to which the offsets are updated
334
     */
335
    private void updateChildOffsetsToIndex(int index)
336
    {
337
      int pos = lastValidOffset.getChildView().getStartOffset();
338
      int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
339
      float lastOffset = lastValidOffset.getMajorOffset();
340
      for (int i = startIndex; i <= index; i++)
341
        {
342
          ChildState cs = getChildState(i);
343
          cs.setMajorOffset(lastOffset);
344
          lastOffset += cs.getMajorSpan();
345
        }
346
    }
347
  }
348
 
349
  /**
350
   * Represents the layout state of a child view.
351
   */
352
  public class ChildState
353
    implements Runnable
354
  {
355
 
356
    /**
357
     * The child view for this state record.
358
     */
359
    private View childView;
360
 
361
    /**
362
     * Indicates if the minor axis requirements of this child view are valid
363
     * or not.
364
     */
365
    private boolean minorValid;
366
 
367
    /**
368
     * Indicates if the major axis requirements of this child view are valid
369
     * or not.
370
     */
371
    private boolean majorValid;
372
 
373
    /**
374
     * Indicates if the current child size is valid. This is package private
375
     * to avoid synthetic accessor method.
376
     */
377
    boolean childSizeValid;
378
 
379
    /**
380
     * The child views minimumSpan. This is package private to avoid accessor
381
     * method.
382
     */
383
    float minimum;
384
 
385
    /**
386
     * The child views preferredSpan. This is package private to avoid accessor
387
     * method.
388
     */
389
    float preferred;
390
 
391
    /**
392
     * The current span of the child view along the major axis.
393
     */
394
    private float majorSpan;
395
 
396
    /**
397
     * The current offset of the child view along the major axis.
398
     */
399
    private float majorOffset;
400
 
401
    /**
402
     * The current span of the child view along the minor axis.
403
     */
404
    private float minorSpan;
405
 
406
    /**
407
     * The current offset of the child view along the major axis.
408
     */
409
    private float minorOffset;
410
 
411
    /**
412
     * The child views maximumSpan.
413
     */
414
    private float maximum;
415
 
416
    /**
417
     * Creates a new <code>ChildState</code> object for the specified child
418
     * view.
419
     *
420
     * @param view the child view for which to create the state record
421
     */
422
    public ChildState(View view)
423
    {
424
      childView = view;
425
    }
426
 
427
    /**
428
     * Returns the child view for which this <code>ChildState</code> represents
429
     * the layout state.
430
     *
431
     * @return the child view for this child state object
432
     */
433
    public View getChildView()
434
    {
435
      return childView;
436
    }
437
 
438
    /**
439
     * Returns <code>true</code> if the current layout information is valid,
440
     * <code>false</code> otherwise.
441
     *
442
     * @return <code>true</code> if the current layout information is valid,
443
     *         <code>false</code> otherwise
444
     */
445
    public boolean isLayoutValid()
446
    {
447
      return minorValid && majorValid && childSizeValid;
448
    }
449
 
450
    /**
451
     * Performs the layout update for the child view managed by this
452
     * <code>ChildState</code>.
453
     */
454
    public void run()
455
    {
456
      Document doc = getDocument();
457
      if (doc instanceof AbstractDocument)
458
        {
459
          AbstractDocument abstractDoc = (AbstractDocument) doc;
460
          abstractDoc.readLock();
461
        }
462
 
463
      try
464
        {
465
 
466
          if (!(minorValid &&  majorValid && childSizeValid)
467
              && childView.getParent() == AsyncBoxView.this)
468
            {
469
              synchronized(AsyncBoxView.this)
470
              {
471
                changing = this;
472
              }
473
              update();
474
              synchronized(AsyncBoxView.this)
475
              {
476
                changing = null;
477
              }
478
              // Changing the major axis may cause the minor axis
479
              // requirements to have changed, so we need to do this again.
480
              update();
481
            }
482
        }
483
      finally
484
        {
485
          if (doc instanceof AbstractDocument)
486
            {
487
              AbstractDocument abstractDoc = (AbstractDocument) doc;
488
              abstractDoc.readUnlock();
489
            }
490
        }
491
    }
492
 
493
    /**
494
     * Performs the actual update after the run methods has made its checks
495
     * and locked the document.
496
     */
497
    private void update()
498
    {
499
      int majorAxis = getMajorAxis();
500
      boolean minorUpdated = false;
501
      synchronized (this)
502
        {
503
          if (! minorValid)
504
            {
505
              int minorAxis = getMinorAxis();
506
              minimum = childView.getMinimumSpan(minorAxis);
507
              preferred = childView.getPreferredSpan(minorAxis);
508
              maximum = childView.getMaximumSpan(minorAxis);
509
              minorValid = true;
510
              minorUpdated = true;
511
            }
512
        }
513
      if (minorUpdated)
514
        minorRequirementChange(this);
515
 
516
      boolean majorUpdated = false;
517
      float delta = 0.0F;
518
      synchronized (this)
519
        {
520
          if (! majorValid)
521
            {
522
              float oldSpan = majorSpan;
523
              majorSpan = childView.getPreferredSpan(majorAxis);
524
              delta = majorSpan - oldSpan;
525
              majorValid = true;
526
              majorUpdated = true;
527
            }
528
        }
529
      if (majorUpdated)
530
        {
531
          majorRequirementChange(this, delta);
532
          locator.childChanged(this);
533
        }
534
 
535
      synchronized (this)
536
        {
537
          if (! childSizeValid)
538
            {
539
              float w;
540
              float h;
541
              if (majorAxis == X_AXIS)
542
                {
543
                  w = majorSpan;
544
                  h = getMinorSpan();
545
                }
546
              else
547
                {
548
                  w = getMinorSpan();
549
                  h = majorSpan;
550
                }
551
              childSizeValid = true;
552
              childView.setSize(w, h);
553
            }
554
        }
555
    }
556
 
557
    /**
558
     * Returns the span of the child view along the minor layout axis.
559
     *
560
     * @return the span of the child view along the minor layout axis
561
     */
562
    public float getMinorSpan()
563
    {
564
      float retVal;
565
      if (maximum < minorSpan)
566
        retVal = maximum;
567
      else
568
        retVal = Math.max(minimum, minorSpan);
569
      return retVal;
570
    }
571
 
572
    /**
573
     * Returns the offset of the child view along the minor layout axis.
574
     *
575
     * @return the offset of the child view along the minor layout axis
576
     */
577
    public float getMinorOffset()
578
    {
579
      float retVal;
580
      if (maximum < minorSpan)
581
        {
582
          float align = childView.getAlignment(getMinorAxis());
583
          retVal = ((minorSpan - maximum) * align);
584
        }
585
      else
586
        retVal = 0f;
587
 
588
      return retVal;
589
    }
590
 
591
    /**
592
     * Returns the span of the child view along the major layout axis.
593
     *
594
     * @return the span of the child view along the major layout axis
595
     */
596
 
597
    public float getMajorSpan()
598
    {
599
      return majorSpan;
600
    }
601
 
602
    /**
603
     * Returns the offset of the child view along the major layout axis.
604
     *
605
     * @return the offset of the child view along the major layout axis
606
     */
607
    public float getMajorOffset()
608
    {
609
      return majorOffset;
610
    }
611
 
612
    /**
613
     * Sets the offset of the child view along the major layout axis. This
614
     * should only be called by the ChildLocator of that child view.
615
     *
616
     * @param offset the offset to set
617
     */
618
    public void setMajorOffset(float offset)
619
    {
620
      majorOffset = offset;
621
    }
622
 
623
    /**
624
     * Mark the preferences changed for that child. This forwards to
625
     * {@link AsyncBoxView#preferenceChanged}.
626
     *
627
     * @param width <code>true</code> if the width preference has changed
628
     * @param height <code>true</code> if the height preference has changed
629
     */
630
    public void preferenceChanged(boolean width, boolean height)
631
    {
632
      if (getMajorAxis() == X_AXIS)
633
        {
634
          if (width)
635
            majorValid = false;
636
          if (height)
637
            minorValid = false;
638
        }
639
      else
640
        {
641
          if (width)
642
            minorValid = false;
643
          if (height)
644
            majorValid = false;
645
        }
646
      childSizeValid = false;
647
    }
648
  }
649
 
650
  /**
651
   * Flushes the requirements changes upwards asynchronously.
652
   */
653
  private class FlushTask implements Runnable
654
  {
655
    /**
656
     * Starts the flush task. This obtains a readLock on the document
657
     * and then flushes all the updates using
658
     * {@link AsyncBoxView#flushRequirementChanges()} after updating the
659
     * requirements.
660
     */
661
    public void run()
662
    {
663
      try
664
        {
665
          // Acquire a lock on the document.
666
          Document doc = getDocument();
667
          if (doc instanceof AbstractDocument)
668
            {
669
              AbstractDocument abstractDoc = (AbstractDocument) doc;
670
              abstractDoc.readLock();
671
            }
672
 
673
          int n = getViewCount();
674
          if (minorChanged && (n > 0))
675
            {
676
              LayoutQueue q = getLayoutQueue();
677
              ChildState min = getChildState(0);
678
              ChildState pref = getChildState(0);
679
              for (int i = 1; i < n; i++)
680
                {
681
                  ChildState cs = getChildState(i);
682
                  if (cs.minimum > min.minimum)
683
                    min = cs;
684
                  if (cs.preferred > pref.preferred)
685
                    pref = cs;
686
                }
687
              synchronized (AsyncBoxView.this)
688
              {
689
                minReq = min;
690
                prefReq = pref;
691
              }
692
            }
693
 
694
          flushRequirementChanges();
695
        }
696
      finally
697
      {
698
        // Release the lock on the document.
699
        Document doc = getDocument();
700
        if (doc instanceof AbstractDocument)
701
          {
702
            AbstractDocument abstractDoc = (AbstractDocument) doc;
703
            abstractDoc.readUnlock();
704
          }
705
      }
706
    }
707
 
708
  }
709
 
710
  /**
711
   * The major layout axis.
712
   */
713
  private int majorAxis;
714
 
715
  /**
716
   * The top inset.
717
   */
718
  private float topInset;
719
 
720
  /**
721
   * The bottom inset.
722
   */
723
  private float bottomInset;
724
 
725
  /**
726
   * The left inset.
727
   */
728
  private float leftInset;
729
 
730
  /**
731
   * Indicates if the major span should be treated as beeing estimated or not.
732
   */
733
  private boolean estimatedMajorSpan;
734
 
735
  /**
736
   * The right inset.
737
   */
738
  private float rightInset;
739
 
740
  /**
741
   * The children and their layout statistics.
742
   */
743
  private ArrayList childStates;
744
 
745
  /**
746
   * The currently changing child state. May be null if there is no child state
747
   * updating at the moment. This is package private to avoid a synthetic
748
   * accessor method inside ChildState.
749
   */
750
  ChildState changing;
751
 
752
  /**
753
   * Represents the minimum requirements. This is used in
754
   * {@link #getMinimumSpan(int)}.
755
   */
756
  ChildState minReq;
757
 
758
  /**
759
   * Represents the minimum requirements. This is used in
760
   * {@link #getPreferredSpan(int)}.
761
   */
762
  ChildState prefReq;
763
 
764
  /**
765
   * Indicates that the major axis requirements have changed.
766
   */
767
  private boolean majorChanged;
768
 
769
  /**
770
   * Indicates that the minor axis requirements have changed. This is package
771
   * private to avoid synthetic accessor method.
772
   */
773
  boolean minorChanged;
774
 
775
  /**
776
   * The current span along the major layout axis. This is package private to
777
   * avoid synthetic accessor method.
778
   */
779
  float majorSpan;
780
 
781
  /**
782
   * The current span along the minor layout axis. This is package private to
783
   * avoid synthetic accessor method.
784
   */
785
  float minorSpan;
786
 
787
  /**
788
   * This tasked is placed on the layout queue to flush updates up to the
789
   * parent view.
790
   */
791
  private Runnable flushTask;
792
 
793
  /**
794
   * The child locator for this view.
795
   */
796
  protected ChildLocator locator;
797
 
798
  /**
799
   * Creates a new <code>AsyncBoxView</code> that represents the specified
800
   * element and layouts its children along the specified axis.
801
   *
802
   * @param elem the element
803
   * @param axis the layout axis
804
   */
805
  public AsyncBoxView(Element elem, int axis)
806
  {
807
    super(elem);
808
    majorAxis = axis;
809
    childStates = new ArrayList();
810
    flushTask = new FlushTask();
811
    locator = new ChildLocator();
812
    minorSpan = Short.MAX_VALUE;
813
  }
814
 
815
  /**
816
   * Returns the major layout axis.
817
   *
818
   * @return the major layout axis
819
   */
820
  public int getMajorAxis()
821
  {
822
    return majorAxis;
823
  }
824
 
825
  /**
826
   * Returns the minor layout axis, that is the axis orthogonal to the major
827
   * layout axis.
828
   *
829
   * @return the minor layout axis
830
   */
831
  public int getMinorAxis()
832
  {
833
    return majorAxis == X_AXIS ? Y_AXIS : X_AXIS;
834
  }
835
 
836
  /**
837
   * Returns the view at the specified <code>index</code>.
838
   *
839
   * @param index the index of the requested child view
840
   *
841
   * @return the view at the specified <code>index</code>
842
   */
843
  public View getView(int index)
844
  {
845
    View view = null;
846
    synchronized(childStates)
847
      {
848
        if ((index >= 0) && (index < childStates.size()))
849
          {
850
            ChildState cs = (ChildState) childStates.get(index);
851
            view = cs.getChildView();
852
          }
853
      }
854
    return view;
855
  }
856
 
857
  /**
858
   * Returns the number of child views.
859
   *
860
   * @return the number of child views
861
   */
862
  public int getViewCount()
863
  {
864
    synchronized(childStates)
865
    {
866
      return childStates.size();
867
    }
868
  }
869
 
870
  /**
871
   * Returns the view index of the child view that represents the specified
872
   * model position.
873
   *
874
   * @param pos the model position for which we search the view index
875
   * @param bias the bias
876
   *
877
   * @return the view index of the child view that represents the specified
878
   *         model position
879
   */
880
  public int getViewIndex(int pos, Position.Bias bias)
881
  {
882
    int retVal = -1;
883
 
884
    if (bias == Position.Bias.Backward)
885
      pos = Math.max(0, pos - 1);
886
 
887
    // TODO: A possible optimization would be to implement a binary search
888
    // here.
889
    int numChildren = childStates.size();
890
    if (numChildren > 0)
891
      {
892
        for (int i = 0; i < numChildren; ++i)
893
          {
894
            View child = ((ChildState) childStates.get(i)).getChildView();
895
            if (child.getStartOffset() <= pos && child.getEndOffset() > pos)
896
              {
897
                retVal = i;
898
                break;
899
              }
900
          }
901
      }
902
    return retVal;
903
  }
904
 
905
  /**
906
   * Returns the top inset.
907
   *
908
   * @return the top inset
909
   */
910
  public float getTopInset()
911
  {
912
    return topInset;
913
  }
914
 
915
  /**
916
   * Sets the top inset.
917
   *
918
   * @param top the top inset
919
   */
920
  public void setTopInset(float top)
921
  {
922
    topInset = top;
923
  }
924
 
925
  /**
926
   * Returns the bottom inset.
927
   *
928
   * @return the bottom inset
929
   */
930
  public float getBottomInset()
931
  {
932
    return bottomInset;
933
  }
934
 
935
  /**
936
   * Sets the bottom inset.
937
   *
938
   * @param bottom the bottom inset
939
   */
940
  public void setBottomInset(float bottom)
941
  {
942
    bottomInset = bottom;
943
  }
944
 
945
  /**
946
   * Returns the left inset.
947
   *
948
   * @return the left inset
949
   */
950
  public float getLeftInset()
951
  {
952
    return leftInset;
953
  }
954
 
955
  /**
956
   * Sets the left inset.
957
   *
958
   * @param left the left inset
959
   */
960
  public void setLeftInset(float left)
961
  {
962
    leftInset = left;
963
  }
964
 
965
  /**
966
   * Returns the right inset.
967
   *
968
   * @return the right inset
969
   */
970
  public float getRightInset()
971
  {
972
    return rightInset;
973
  }
974
 
975
  /**
976
   * Sets the right inset.
977
   *
978
   * @param right the right inset
979
   */
980
  public void setRightInset(float right)
981
  {
982
    rightInset = right;
983
  }
984
 
985
  /**
986
   * Loads the child views of this view. This is triggered by
987
   * {@link #setParent(View)}.
988
   *
989
   * @param f the view factory to build child views with
990
   */
991
  protected void loadChildren(ViewFactory f)
992
  {
993
    Element e = getElement();
994
    int n = e.getElementCount();
995
    if (n > 0)
996
      {
997
        View[] added = new View[n];
998
        for (int i = 0; i < n; i++)
999
          {
1000
            added[i] = f.create(e.getElement(i));
1001
          }
1002
        replace(0, 0, added);
1003
      }
1004
  }
1005
 
1006
  /**
1007
   * Returns the span along an axis that is taken up by the insets.
1008
   *
1009
   * @param axis the axis
1010
   *
1011
   * @return the span along an axis that is taken up by the insets
1012
   *
1013
   * @since 1.4
1014
   */
1015
  protected float getInsetSpan(int axis)
1016
  {
1017
    float span;
1018
    if (axis == X_AXIS)
1019
      span = leftInset + rightInset;
1020
    else
1021
      span = topInset + bottomInset;
1022
    return span;
1023
  }
1024
 
1025
  /**
1026
   * Sets the <code>estimatedMajorSpan</code> property that determines if
1027
   * the major span should be treated as beeing estimated.
1028
   *
1029
   * @param estimated if the major span should be treated as estimated or not
1030
   *
1031
   * @since 1.4
1032
   */
1033
  protected void setEstimatedMajorSpan(boolean estimated)
1034
  {
1035
    estimatedMajorSpan = estimated;
1036
  }
1037
 
1038
  /**
1039
   * Determines whether the major span should be treated as estimated or as
1040
   * beeing accurate.
1041
   *
1042
   * @return <code>true</code> if the major span should be treated as
1043
   *         estimated, <code>false</code> if the major span should be treated
1044
   *         as accurate
1045
   *
1046
   * @since 1.4
1047
   */
1048
  protected boolean getEstimatedMajorSpan()
1049
  {
1050
    return estimatedMajorSpan;
1051
  }
1052
 
1053
  /**
1054
   * Receives notification from the child states that the requirements along
1055
   * the minor axis have changed.
1056
   *
1057
   * @param cs the child state from which this notification is messaged
1058
   */
1059
  protected synchronized void minorRequirementChange(ChildState cs)
1060
  {
1061
    minorChanged = true;
1062
  }
1063
 
1064
  /**
1065
   * Receives notification from the child states that the requirements along
1066
   * the major axis have changed.
1067
   *
1068
   * @param cs the child state from which this notification is messaged
1069
   */
1070
  protected void majorRequirementChange(ChildState cs, float delta)
1071
  {
1072
    if (! estimatedMajorSpan)
1073
      majorSpan += delta;
1074
    majorChanged = true;
1075
  }
1076
 
1077
  /**
1078
   * Sets the parent for this view. This calls loadChildren if
1079
   * <code>parent</code> is not <code>null</code> and there have not been any
1080
   * child views initializes.
1081
   *
1082
   * @param parent the new parent view; <code>null</code> if this view is
1083
   *        removed from the view hierarchy
1084
   *
1085
   * @see View#setParent(View)
1086
   */
1087
  public void setParent(View parent)
1088
  {
1089
    super.setParent(parent);
1090
    if ((parent != null) && (getViewCount() == 0))
1091
      {
1092
        ViewFactory f = getViewFactory();
1093
        loadChildren(f);
1094
      }
1095
  }
1096
 
1097
  /**
1098
   * Sets the size of this view. This is ususally called before {@link #paint}
1099
   * is called to make sure the view has a valid layout.
1100
   *
1101
   * This implementation queues layout requests for every child view if the
1102
   * minor axis span has changed. (The major axis span is requested to never
1103
   * change for this view).
1104
   *
1105
   * @param width the width of the view
1106
   * @param height the height of the view
1107
   */
1108
  public void setSize(float width, float height)
1109
  {
1110
    float targetSpan;
1111
    if (majorAxis == X_AXIS)
1112
      targetSpan = height - getTopInset() - getBottomInset();
1113
    else
1114
      targetSpan = width - getLeftInset() - getRightInset();
1115
 
1116
    if (targetSpan != minorSpan)
1117
      {
1118
        minorSpan = targetSpan;
1119
 
1120
        int n = getViewCount();
1121
        LayoutQueue q = getLayoutQueue();
1122
        for (int i = 0; i < n; i++)
1123
          {
1124
            ChildState cs = getChildState(i);
1125
            cs.childSizeValid = false;
1126
            q.addTask(cs);
1127
          }
1128
        q.addTask(flushTask);
1129
    }
1130
  }
1131
 
1132
  /**
1133
   * Replaces child views with new child views.
1134
   *
1135
   * This creates ChildState objects for all the new views and adds layout
1136
   * requests for them to the layout queue.
1137
   *
1138
   * @param offset the offset at which to remove/insert
1139
   * @param length the number of child views to remove
1140
   * @param views the new child views to insert
1141
   */
1142
  public void replace(int offset, int length, View[] views)
1143
  {
1144
    synchronized(childStates)
1145
      {
1146
        LayoutQueue q = getLayoutQueue();
1147
        for (int i = 0; i < length; i++)
1148
          childStates.remove(offset);
1149
 
1150
        for (int i = views.length - 1; i >= 0; i--)
1151
          childStates.add(offset, createChildState(views[i]));
1152
 
1153
        // We need to go through the new child states _after_ they have been
1154
        // added to the childStates list, otherwise the layout tasks may find
1155
        // an incomplete child list. That means we have to loop through
1156
        // them again, but what else can we do?
1157
        if (views.length != 0)
1158
          {
1159
            for (int i = 0; i < views.length; i++)
1160
              {
1161
                ChildState cs = (ChildState) childStates.get(i + offset);
1162
                cs.getChildView().setParent(this);
1163
                q.addTask(cs);
1164
              }
1165
            q.addTask(flushTask);
1166
          }
1167
      }
1168
  }
1169
 
1170
  /**
1171
   * Paints the view. This requests the {@link ChildLocator} to paint the views
1172
   * after setting the allocation on it.
1173
   *
1174
   * @param g the graphics context to use
1175
   * @param s the allocation for this view
1176
   */
1177
  public void paint(Graphics g, Shape s)
1178
  {
1179
    synchronized (locator)
1180
      {
1181
        locator.setAllocation(s);
1182
        locator.paintChildren(g);
1183
      }
1184
  }
1185
 
1186
  /**
1187
   * Returns the preferred span of this view along the specified layout axis.
1188
   *
1189
   * @return the preferred span of this view along the specified layout axis
1190
   */
1191
  public float getPreferredSpan(int axis)
1192
  {
1193
    float retVal;
1194
    if (majorAxis == axis)
1195
      retVal = majorSpan;
1196
 
1197
    else if (prefReq != null)
1198
      {
1199
        View child = prefReq.getChildView();
1200
        retVal = child.getPreferredSpan(axis);
1201
      }
1202
 
1203
    // If we have no layout information yet, then return insets + 30 as
1204
    // an estimation.
1205
    else
1206
      {
1207
        if (axis == X_AXIS)
1208
          retVal = getLeftInset() + getRightInset() + 30;
1209
        else
1210
          retVal = getTopInset() + getBottomInset() + 30;
1211
      }
1212
    return retVal;
1213
  }
1214
 
1215
  /**
1216
   * Maps a model location to view coordinates.
1217
   *
1218
   * @param pos the model location
1219
   * @param a the current allocation of this view
1220
   * @param b the bias
1221
   *
1222
   * @return the view allocation for the specified model location
1223
   */
1224
  public Shape modelToView(int pos, Shape a, Bias b)
1225
    throws BadLocationException
1226
  {
1227
    int index = getViewIndexAtPosition(pos, b);
1228
    Shape ca = locator.getChildAllocation(index, a);
1229
 
1230
    ChildState cs = getChildState(index);
1231
    synchronized (cs)
1232
      {
1233
        View cv = cs.getChildView();
1234
        Shape v = cv.modelToView(pos, ca, b);
1235
        return v;
1236
      }
1237
  }
1238
 
1239
  /**
1240
   * Maps view coordinates to a model location.
1241
   *
1242
   * @param x the x coordinate (relative to <code>a</code>)
1243
   * @param y the y coordinate (relative to <code>a</code>)
1244
   * @param b holds the bias of the model location on method exit
1245
   *
1246
   * @return the model location for the specified view location
1247
   */
1248
  public int viewToModel(float x, float y, Shape a, Bias[] b)
1249
  {
1250
    int pos;
1251
    int index;
1252
    Shape ca;
1253
 
1254
    synchronized (locator)
1255
      {
1256
        index = locator.getViewIndexAtPoint(x, y, a);
1257
        ca = locator.getChildAllocation(index, a);
1258
      }
1259
 
1260
    ChildState cs = getChildState(index);
1261
    synchronized (cs)
1262
      {
1263
        View v = cs.getChildView();
1264
        pos = v.viewToModel(x, y, ca, b);
1265
      }
1266
    return pos;
1267
  }
1268
 
1269
  /**
1270
   * Returns the child allocation for the child view with the specified
1271
   * <code>index</code>.
1272
   *
1273
   * @param index the index of the child view
1274
   * @param a the current allocation of this view
1275
   *
1276
   * @return the allocation of the child view
1277
   */
1278
  public Shape getChildAllocation(int index, Shape a)
1279
  {
1280
    Shape ca = locator.getChildAllocation(index, a);
1281
    return ca;
1282
  }
1283
 
1284
  /**
1285
   * Returns the maximum span of this view along the specified axis.
1286
   * This is implemented to return the <code>preferredSpan</code> for the
1287
   * major axis (that means the box can't be resized along the major axis) and
1288
   * {@link Short#MAX_VALUE} for the minor axis.
1289
   *
1290
   * @param axis the axis
1291
   *
1292
   * @return the maximum span of this view along the specified axis
1293
   */
1294
  public float getMaximumSpan(int axis)
1295
  {
1296
    float max;
1297
    if (axis == majorAxis)
1298
      max = getPreferredSpan(axis);
1299
    else
1300
      max = Short.MAX_VALUE;
1301
    return max;
1302
  }
1303
 
1304
  /**
1305
   * Returns the minimum span along the specified axis.
1306
   */
1307
  public float getMinimumSpan(int axis)
1308
  {
1309
    float min;
1310
    if (axis == majorAxis)
1311
      min = getPreferredSpan(axis);
1312
    else
1313
      {
1314
        if (minReq != null)
1315
          {
1316
            View child = minReq.getChildView();
1317
            min = child.getMinimumSpan(axis);
1318
          }
1319
        else
1320
          {
1321
            // No layout information yet. Return insets + 5 as some kind of
1322
            // estimation.
1323
            if (axis == X_AXIS)
1324
              min = getLeftInset() + getRightInset() + 5;
1325
            else
1326
              min = getTopInset() + getBottomInset() + 5;
1327
          }
1328
      }
1329
    return min;
1330
  }
1331
 
1332
  /**
1333
   * Receives notification that one of the child views has changed its
1334
   * layout preferences along one or both axis.
1335
   *
1336
   * This queues a layout request for that child view if necessary.
1337
   *
1338
   * @param view the view that has changed its preferences
1339
   * @param width <code>true</code> if the width preference has changed
1340
   * @param height <code>true</code> if the height preference has changed
1341
   */
1342
  public synchronized void preferenceChanged(View view, boolean width,
1343
                                             boolean height)
1344
  {
1345
    if (view == null)
1346
      getParent().preferenceChanged(this, width, height);
1347
    else
1348
      {
1349
        if (changing != null)
1350
          {
1351
            View cv = changing.getChildView();
1352
            if (cv == view)
1353
              {
1354
                changing.preferenceChanged(width, height);
1355
                return;
1356
              }
1357
          }
1358
        int index = getViewIndexAtPosition(view.getStartOffset(),
1359
                                           Position.Bias.Forward);
1360
        ChildState cs = getChildState(index);
1361
        cs.preferenceChanged(width, height);
1362
        LayoutQueue q = getLayoutQueue();
1363
        q.addTask(cs);
1364
        q.addTask(flushTask);
1365
      }
1366
  }
1367
 
1368
  /**
1369
   * Updates the layout for this view. This is implemented to trigger
1370
   * {@link ChildLocator#childChanged} for the changed view, if there is
1371
   * any.
1372
   *
1373
   * @param ec the element change, may be <code>null</code> if there were
1374
   *        no changes to the element of this view
1375
   * @param e the document event
1376
   * @param a the current allocation of this view
1377
   */
1378
  protected void updateLayout(DocumentEvent.ElementChange ec,
1379
                              DocumentEvent e, Shape a)
1380
  {
1381
    if (ec != null)
1382
      {
1383
        int index = Math.max(ec.getIndex() - 1, 0);
1384
        ChildState cs = getChildState(index);
1385
        locator.childChanged(cs);
1386
      }
1387
  }
1388
 
1389
 
1390
  /**
1391
   * Returns the <code>ChildState</code> object associated with the child view
1392
   * at the specified <code>index</code>.
1393
   *
1394
   * @param index the index of the child view for which to query the state
1395
   *
1396
   * @return the child state for the specified child view
1397
   */
1398
  protected ChildState getChildState(int index) {
1399
    synchronized (childStates)
1400
      {
1401
        return (ChildState) childStates.get(index);
1402
      }
1403
  }
1404
 
1405
  /**
1406
   * Returns the <code>LayoutQueue</code> used for layouting the box view.
1407
   * This simply returns {@link LayoutQueue#getDefaultQueue()}.
1408
   *
1409
   * @return the <code>LayoutQueue</code> used for layouting the box view
1410
   */
1411
  protected LayoutQueue getLayoutQueue()
1412
  {
1413
    return LayoutQueue.getDefaultQueue();
1414
  }
1415
 
1416
  /**
1417
   * Returns the child view index of the view that represents the specified
1418
   * position in the document model.
1419
   *
1420
   * @param pos the position in the model
1421
   * @param b the bias
1422
   *
1423
   * @return the child view index of the view that represents the specified
1424
   *         position in the document model
1425
   */
1426
  protected synchronized int getViewIndexAtPosition(int pos, Position.Bias b)
1427
  {
1428
    if (b == Position.Bias.Backward)
1429
      pos = Math.max(0, pos - 1);
1430
    Element elem = getElement();
1431
    return elem.getElementIndex(pos);
1432
  }
1433
 
1434
  /**
1435
   * Creates a <code>ChildState</code> object for the specified view.
1436
   *
1437
   * @param v the view for which to create a child state object
1438
   *
1439
   * @return the created child state
1440
   */
1441
  protected ChildState createChildState(View v)
1442
  {
1443
    return new ChildState(v);
1444
  }
1445
 
1446
  /**
1447
   * Flushes the requirements changes upwards to the parent view. This is
1448
   * called from the layout thread.
1449
   */
1450
  protected synchronized void flushRequirementChanges()
1451
  {
1452
    if (majorChanged || minorChanged)
1453
      {
1454
        View p = getParent();
1455
        if (p != null)
1456
          {
1457
            boolean horizontal;
1458
            boolean vertical;
1459
            if (majorAxis == X_AXIS)
1460
              {
1461
                horizontal = majorChanged;
1462
                vertical = minorChanged;
1463
              }
1464
            else
1465
              {
1466
                vertical = majorChanged;
1467
                horizontal = minorChanged;
1468
              }
1469
 
1470
            p.preferenceChanged(this, horizontal, vertical);
1471
            majorChanged = false;
1472
            minorChanged = false;
1473
 
1474
            Component c = getContainer();
1475
            if (c != null)
1476
              c.repaint();
1477
          }
1478
      }
1479
  }
1480
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.