1 /*
2 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
53
54 /**
55 * Constructs a FlowView for the given element.
56 *
57 * @param elem the element that this view is responsible for
58 * @param axis may be either View.X_AXIS or View.Y_AXIS
59 */
60 public FlowView(Element elem, int axis) {
61 super(elem, axis);
62 layoutSpan = Integer.MAX_VALUE;
63 strategy = new FlowStrategy();
64 }
65
66 /**
67 * Fetches the axis along which views should be
68 * flowed. By default, this will be the axis
69 * orthogonal to the axis along which the flow
70 * rows are tiled (the axis of the default flow
71 * rows themselves). This is typically used
72 * by the <code>FlowStrategy</code>.
73 */
74 public int getFlowAxis() {
75 if (getAxis() == Y_AXIS) {
76 return X_AXIS;
77 }
78 return Y_AXIS;
79 }
80
81 /**
82 * Fetch the constraining span to flow against for
83 * the given child index. This is called by the
84 * FlowStrategy while it is updating the flow.
85 * A flow can be shaped by providing different values
86 * for the row constraints. By default, the entire
87 * span inside of the insets along the flow axis
88 * is returned.
89 *
90 * @param index the index of the row being updated.
91 * This should be a value >= 0 and < getViewCount().
92 * @see #getFlowStart
93 */
94 public int getFlowSpan(int index) {
95 return layoutSpan;
96 }
97
98 /**
99 * Fetch the location along the flow axis that the
100 * flow span will start at. This is called by the
101 * FlowStrategy while it is updating the flow.
102 * A flow can be shaped by providing different values
103 * for the row constraints.
104
105 * @param index the index of the row being updated.
106 * This should be a value >= 0 and < getViewCount().
107 * @see #getFlowSpan
108 */
109 public int getFlowStart(int index) {
110 return 0;
111 }
112
113 /**
114 * Create a View that should be used to hold a
115 * a rows worth of children in a flow. This is
116 * called by the FlowStrategy when new children
117 * are added or removed (i.e. rows are added or
118 * removed) in the process of updating the flow.
119 */
120 protected abstract View createRow();
121
122 // ---- BoxView methods -------------------------------------
123
124 /**
125 * Loads all of the children to initialize the view.
126 * This is called by the <code>setParent</code> method.
127 * This is reimplemented to not load any children directly
128 * (as they are created in the process of formatting).
129 * If the layoutPool variable is null, an instance of
130 * LogicalView is created to represent the logical view
131 * that is used in the process of formatting.
132 *
133 * @param f the view factory
134 */
135 protected void loadChildren(ViewFactory f) {
136 if (layoutPool == null) {
137 layoutPool = new LogicalView(getElement());
138 }
342 try {
343 damageStart = fv.getDocument().createPosition(offset);
344 } catch (BadLocationException e) {
345 // shouldn't happen since offset is inside view bounds
346 assert(false);
347 }
348 }
349 }
350 }
351
352 void unsetDamage() {
353 damageStart = null;
354 }
355
356 /**
357 * Gives notification that something was inserted into the document
358 * in a location that the given flow view is responsible for. The
359 * strategy should update the appropriate changed region (which
360 * depends upon the strategy used for repair).
361 *
362 * @param e the change information from the associated document
363 * @param alloc the current allocation of the view inside of the insets.
364 * This value will be null if the view has not yet been displayed.
365 * @see View#insertUpdate
366 */
367 public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
368 // FlowView.loadChildren() makes a synthetic call into this,
369 // passing null as e
370 if (e != null) {
371 addDamage(fv, e.getOffset());
372 }
373
374 if (alloc != null) {
375 Component host = fv.getContainer();
376 if (host != null) {
377 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
378 }
379 } else {
380 fv.preferenceChanged(null, true, true);
381 }
382 }
383
384 /**
385 * Gives notification that something was removed from the document
386 * in a location that the given flow view is responsible for.
387 *
388 * @param e the change information from the associated document
389 * @param alloc the current allocation of the view inside of the insets.
390 * @see View#removeUpdate
391 */
392 public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
393 addDamage(fv, e.getOffset());
394 if (alloc != null) {
395 Component host = fv.getContainer();
396 if (host != null) {
397 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
398 }
399 } else {
400 fv.preferenceChanged(null, true, true);
401 }
402 }
403
404 /**
405 * Gives notification from the document that attributes were changed
406 * in a location that this view is responsible for.
407 *
409 * @param e the <code>DocumentEvent</code> describing the changes
410 * done to the Document
411 * @param alloc Bounds of the View
412 * @see View#changedUpdate
413 */
414 public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
415 addDamage(fv, e.getOffset());
416 if (alloc != null) {
417 Component host = fv.getContainer();
418 if (host != null) {
419 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
420 }
421 } else {
422 fv.preferenceChanged(null, true, true);
423 }
424 }
425
426 /**
427 * This method gives flow strategies access to the logical
428 * view of the FlowView.
429 */
430 protected View getLogicalView(FlowView fv) {
431 return fv.layoutPool;
432 }
433
434 /**
435 * Update the flow on the given FlowView. By default, this causes
436 * all of the rows (child views) to be rebuilt to match the given
437 * constraints for each row. This is called by a FlowView.layout
438 * to update the child views in the flow.
439 *
440 * @param fv the view to reflow
441 */
442 public void layout(FlowView fv) {
443 View pool = getLogicalView(fv);
444 int rowIndex, p0;
445 int p1 = fv.getEndOffset();
446
447 if (fv.majorAllocValid) {
448 if (damageStart == null) {
478 rowIndex++;
479 }
480 viewBuffer = null;
481
482 if (rowIndex < rowCount) {
483 fv.replace(rowIndex, rowCount - rowIndex, null);
484 }
485 unsetDamage();
486 }
487
488 /**
489 * Creates a row of views that will fit within the
490 * layout span of the row. This is called by the layout method.
491 * This is implemented to fill the row by repeatedly calling
492 * the createView method until the available span has been
493 * exhausted, a forced break was encountered, or the createView
494 * method returned null. If the remaining span was exhausted,
495 * the adjustRow method will be called to perform adjustments
496 * to the row to try and make it fit into the given span.
497 *
498 * @param rowIndex the index of the row to fill in with views. The
499 * row is assumed to be empty on entry.
500 * @param pos The current position in the children of
501 * this views element from which to start.
502 * @return the position to start the next row
503 */
504 protected int layoutRow(FlowView fv, int rowIndex, int pos) {
505 View row = fv.getView(rowIndex);
506 float x = fv.getFlowStart(rowIndex);
507 float spanLeft = fv.getFlowSpan(rowIndex);
508 int end = fv.getEndOffset();
509 TabExpander te = (fv instanceof TabExpander) ? (TabExpander)fv : null;
510 final int flowAxis = fv.getFlowAxis();
511
512 int breakWeight = BadBreakWeight;
513 float breakX = 0f;
514 float breakSpan = 0f;
515 int breakIndex = -1;
516 int n = 0;
517
561 spanLeft -= chunkSpan;
562 x += chunkSpan;
563 viewBuffer.add(v);
564 pos = v.getEndOffset();
565 n++;
566 }
567
568 View[] views = new View[viewBuffer.size()];
569 viewBuffer.toArray(views);
570 row.replace(0, row.getViewCount(), views);
571 return (views.length > 0 ? row.getEndOffset() : pos);
572 }
573
574 /**
575 * Adjusts the given row if possible to fit within the
576 * layout span. By default this will try to find the
577 * highest break weight possible nearest the end of
578 * the row. If a forced break is encountered, the
579 * break will be positioned there.
580 *
581 * @param rowIndex the row to adjust to the current layout
582 * span.
583 * @param desiredSpan the current layout span >= 0
584 * @param x the location r starts at.
585 */
586 protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
587 final int flowAxis = fv.getFlowAxis();
588 View r = fv.getView(rowIndex);
589 int n = r.getViewCount();
590 int span = 0;
591 int bestWeight = BadBreakWeight;
592 int bestSpan = 0;
593 int bestIndex = -1;
594 View v;
595 for (int i = 0; i < n; i++) {
596 v = r.getView(i);
597 int spanLeft = desiredSpan - span;
598
599 int w = v.getBreakWeight(flowAxis, x + span, spanLeft);
600 if ((w >= bestWeight) && (w > BadBreakWeight)) {
637 }
638
639 void reparentViews(View pool, int startPos) {
640 int n = pool.getViewIndex(startPos, Position.Bias.Forward);
641 if (n >= 0) {
642 for (int i = n; i < pool.getViewCount(); i++) {
643 pool.getView(i).setParent(pool);
644 }
645 }
646 }
647
648 /**
649 * Creates a view that can be used to represent the current piece
650 * of the flow. This can be either an entire view from the
651 * logical view, or a fragment of the logical view.
652 *
653 * @param fv the view holding the flow
654 * @param startOffset the start location for the view being created
655 * @param spanLeft the about of span left to fill in the row
656 * @param rowIndex the row the view will be placed into
657 */
658 protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
659 // Get the child view that contains the given starting position
660 View lv = getLogicalView(fv);
661 int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
662 View v = lv.getView(childIndex);
663 if (startOffset==v.getStartOffset()) {
664 // return the entire view
665 return v;
666 }
667
668 // return a fragment.
669 v = v.createFragment(startOffset, v.getEndOffset());
670 return v;
671 }
672 }
673
674 /**
675 * This class can be used to represent a logical view for
676 * a flow. It keeps the children updated to reflect the state
|
1 /*
2 * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
53
54 /**
55 * Constructs a FlowView for the given element.
56 *
57 * @param elem the element that this view is responsible for
58 * @param axis may be either View.X_AXIS or View.Y_AXIS
59 */
60 public FlowView(Element elem, int axis) {
61 super(elem, axis);
62 layoutSpan = Integer.MAX_VALUE;
63 strategy = new FlowStrategy();
64 }
65
66 /**
67 * Fetches the axis along which views should be
68 * flowed. By default, this will be the axis
69 * orthogonal to the axis along which the flow
70 * rows are tiled (the axis of the default flow
71 * rows themselves). This is typically used
72 * by the <code>FlowStrategy</code>.
73 * @return the axis along which views should be
74 * flowed
75 */
76 public int getFlowAxis() {
77 if (getAxis() == Y_AXIS) {
78 return X_AXIS;
79 }
80 return Y_AXIS;
81 }
82
83 /**
84 * Fetch the constraining span to flow against for
85 * the given child index. This is called by the
86 * FlowStrategy while it is updating the flow.
87 * A flow can be shaped by providing different values
88 * for the row constraints. By default, the entire
89 * span inside of the insets along the flow axis
90 * is returned.
91 *
92 * @param index the index of the row being updated.
93 * This should be a value >= 0 and < getViewCount().
94 * @return the constraining span to flow against for
95 * the given child index
96 * @see #getFlowStart
97 */
98 public int getFlowSpan(int index) {
99 return layoutSpan;
100 }
101
102 /**
103 * Fetch the location along the flow axis that the
104 * flow span will start at. This is called by the
105 * FlowStrategy while it is updating the flow.
106 * A flow can be shaped by providing different values
107 * for the row constraints.
108
109 * @param index the index of the row being updated.
110 * This should be a value >= 0 and < getViewCount().
111 * @return the location along the flow axis that the
112 * flow span will start at
113 * @see #getFlowSpan
114 */
115 public int getFlowStart(int index) {
116 return 0;
117 }
118
119 /**
120 * Create a View that should be used to hold a
121 * a rows worth of children in a flow. This is
122 * called by the FlowStrategy when new children
123 * are added or removed (i.e. rows are added or
124 * removed) in the process of updating the flow.
125 * @return a View that should be used to hold a
126 * a rows worth of children in a flow
127 */
128 protected abstract View createRow();
129
130 // ---- BoxView methods -------------------------------------
131
132 /**
133 * Loads all of the children to initialize the view.
134 * This is called by the <code>setParent</code> method.
135 * This is reimplemented to not load any children directly
136 * (as they are created in the process of formatting).
137 * If the layoutPool variable is null, an instance of
138 * LogicalView is created to represent the logical view
139 * that is used in the process of formatting.
140 *
141 * @param f the view factory
142 */
143 protected void loadChildren(ViewFactory f) {
144 if (layoutPool == null) {
145 layoutPool = new LogicalView(getElement());
146 }
350 try {
351 damageStart = fv.getDocument().createPosition(offset);
352 } catch (BadLocationException e) {
353 // shouldn't happen since offset is inside view bounds
354 assert(false);
355 }
356 }
357 }
358 }
359
360 void unsetDamage() {
361 damageStart = null;
362 }
363
364 /**
365 * Gives notification that something was inserted into the document
366 * in a location that the given flow view is responsible for. The
367 * strategy should update the appropriate changed region (which
368 * depends upon the strategy used for repair).
369 *
370 * @param fv the flow view
371 * @param e the change information from the associated document
372 * @param alloc the current allocation of the view inside of the insets.
373 * This value will be null if the view has not yet been displayed.
374 * @see View#insertUpdate
375 */
376 public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
377 // FlowView.loadChildren() makes a synthetic call into this,
378 // passing null as e
379 if (e != null) {
380 addDamage(fv, e.getOffset());
381 }
382
383 if (alloc != null) {
384 Component host = fv.getContainer();
385 if (host != null) {
386 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
387 }
388 } else {
389 fv.preferenceChanged(null, true, true);
390 }
391 }
392
393 /**
394 * Gives notification that something was removed from the document
395 * in a location that the given flow view is responsible for.
396 *
397 * @param fv the flow view
398 * @param e the change information from the associated document
399 * @param alloc the current allocation of the view inside of the insets.
400 * @see View#removeUpdate
401 */
402 public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
403 addDamage(fv, e.getOffset());
404 if (alloc != null) {
405 Component host = fv.getContainer();
406 if (host != null) {
407 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
408 }
409 } else {
410 fv.preferenceChanged(null, true, true);
411 }
412 }
413
414 /**
415 * Gives notification from the document that attributes were changed
416 * in a location that this view is responsible for.
417 *
419 * @param e the <code>DocumentEvent</code> describing the changes
420 * done to the Document
421 * @param alloc Bounds of the View
422 * @see View#changedUpdate
423 */
424 public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
425 addDamage(fv, e.getOffset());
426 if (alloc != null) {
427 Component host = fv.getContainer();
428 if (host != null) {
429 host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
430 }
431 } else {
432 fv.preferenceChanged(null, true, true);
433 }
434 }
435
436 /**
437 * This method gives flow strategies access to the logical
438 * view of the FlowView.
439 * @param fv the FlowView
440 * @return the logical view of the FlowView
441 */
442 protected View getLogicalView(FlowView fv) {
443 return fv.layoutPool;
444 }
445
446 /**
447 * Update the flow on the given FlowView. By default, this causes
448 * all of the rows (child views) to be rebuilt to match the given
449 * constraints for each row. This is called by a FlowView.layout
450 * to update the child views in the flow.
451 *
452 * @param fv the view to reflow
453 */
454 public void layout(FlowView fv) {
455 View pool = getLogicalView(fv);
456 int rowIndex, p0;
457 int p1 = fv.getEndOffset();
458
459 if (fv.majorAllocValid) {
460 if (damageStart == null) {
490 rowIndex++;
491 }
492 viewBuffer = null;
493
494 if (rowIndex < rowCount) {
495 fv.replace(rowIndex, rowCount - rowIndex, null);
496 }
497 unsetDamage();
498 }
499
500 /**
501 * Creates a row of views that will fit within the
502 * layout span of the row. This is called by the layout method.
503 * This is implemented to fill the row by repeatedly calling
504 * the createView method until the available span has been
505 * exhausted, a forced break was encountered, or the createView
506 * method returned null. If the remaining span was exhausted,
507 * the adjustRow method will be called to perform adjustments
508 * to the row to try and make it fit into the given span.
509 *
510 * @param fv the flow view
511 * @param rowIndex the index of the row to fill in with views. The
512 * row is assumed to be empty on entry.
513 * @param pos The current position in the children of
514 * this views element from which to start.
515 * @return the position to start the next row
516 */
517 protected int layoutRow(FlowView fv, int rowIndex, int pos) {
518 View row = fv.getView(rowIndex);
519 float x = fv.getFlowStart(rowIndex);
520 float spanLeft = fv.getFlowSpan(rowIndex);
521 int end = fv.getEndOffset();
522 TabExpander te = (fv instanceof TabExpander) ? (TabExpander)fv : null;
523 final int flowAxis = fv.getFlowAxis();
524
525 int breakWeight = BadBreakWeight;
526 float breakX = 0f;
527 float breakSpan = 0f;
528 int breakIndex = -1;
529 int n = 0;
530
574 spanLeft -= chunkSpan;
575 x += chunkSpan;
576 viewBuffer.add(v);
577 pos = v.getEndOffset();
578 n++;
579 }
580
581 View[] views = new View[viewBuffer.size()];
582 viewBuffer.toArray(views);
583 row.replace(0, row.getViewCount(), views);
584 return (views.length > 0 ? row.getEndOffset() : pos);
585 }
586
587 /**
588 * Adjusts the given row if possible to fit within the
589 * layout span. By default this will try to find the
590 * highest break weight possible nearest the end of
591 * the row. If a forced break is encountered, the
592 * break will be positioned there.
593 *
594 * @param fv the flow view
595 * @param rowIndex the row to adjust to the current layout
596 * span.
597 * @param desiredSpan the current layout span >= 0
598 * @param x the location r starts at.
599 */
600 protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
601 final int flowAxis = fv.getFlowAxis();
602 View r = fv.getView(rowIndex);
603 int n = r.getViewCount();
604 int span = 0;
605 int bestWeight = BadBreakWeight;
606 int bestSpan = 0;
607 int bestIndex = -1;
608 View v;
609 for (int i = 0; i < n; i++) {
610 v = r.getView(i);
611 int spanLeft = desiredSpan - span;
612
613 int w = v.getBreakWeight(flowAxis, x + span, spanLeft);
614 if ((w >= bestWeight) && (w > BadBreakWeight)) {
651 }
652
653 void reparentViews(View pool, int startPos) {
654 int n = pool.getViewIndex(startPos, Position.Bias.Forward);
655 if (n >= 0) {
656 for (int i = n; i < pool.getViewCount(); i++) {
657 pool.getView(i).setParent(pool);
658 }
659 }
660 }
661
662 /**
663 * Creates a view that can be used to represent the current piece
664 * of the flow. This can be either an entire view from the
665 * logical view, or a fragment of the logical view.
666 *
667 * @param fv the view holding the flow
668 * @param startOffset the start location for the view being created
669 * @param spanLeft the about of span left to fill in the row
670 * @param rowIndex the row the view will be placed into
671 * @return a view that can be used to represent the current piece
672 * of the flow
673 */
674 protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
675 // Get the child view that contains the given starting position
676 View lv = getLogicalView(fv);
677 int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
678 View v = lv.getView(childIndex);
679 if (startOffset==v.getStartOffset()) {
680 // return the entire view
681 return v;
682 }
683
684 // return a fragment.
685 v = v.createFragment(startOffset, v.getEndOffset());
686 return v;
687 }
688 }
689
690 /**
691 * This class can be used to represent a logical view for
692 * a flow. It keeps the children updated to reflect the state
|