001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Alexey A. Ivanov
019: * @version $Revision$
020: */package javax.swing.text;
021:
022: import java.awt.Component;
023: import java.awt.Graphics;
024: import java.awt.Rectangle;
025: import java.awt.Shape;
026:
027: import javax.swing.SizeRequirements;
028: import javax.swing.event.DocumentEvent;
029: import javax.swing.text.Position.Bias;
030:
031: import org.apache.harmony.x.swing.internal.nls.Messages;
032:
033: public abstract class FlowView extends BoxView {
034: public static class FlowStrategy {
035: private static FlowStrategy sharedStrategy;
036:
037: public void insertUpdate(final FlowView fv,
038: final DocumentEvent event, final Rectangle alloc) {
039: invalidateFlow(fv, event, alloc);
040: }
041:
042: public void removeUpdate(final FlowView fv,
043: final DocumentEvent event, final Rectangle alloc) {
044: invalidateFlow(fv, event, alloc);
045: }
046:
047: public void changedUpdate(final FlowView fv,
048: final DocumentEvent event, final Rectangle alloc) {
049: invalidateFlow(fv, event, alloc);
050: }
051:
052: public void layout(final FlowView fv) {
053: final View lv = getLogicalView(fv);
054: int offset = lv.getStartOffset();
055: final int endOffset = lv.getEndOffset();
056:
057: fv.removeAll();
058:
059: int rowIndex = 0;
060: do {
061: View row = fv.createRow();
062: fv.append(row);
063: offset = layoutRow(fv, rowIndex++, offset);
064: } while (offset < endOffset);
065: }
066:
067: protected void adjustRow(final FlowView fv, final int rowIndex,
068: final int desiredSpan, final int x) {
069: View row = fv.getView(rowIndex);
070: int maxWeight = BadBreakWeight;
071: int index = -1;
072: View toBreak = null;
073: int breakX = -1;
074: int breakSpan = -1;
075: int span = 0;
076: for (int i = 0; i < row.getViewCount(); i++) {
077: View view = row.getView(i);
078: int viewSpan = (int) view.getPreferredSpan(fv
079: .getFlowAxis());
080: int weight = view.getBreakWeight(fv.getFlowAxis(), x
081: + span, desiredSpan - span);
082: if (weight >= maxWeight) {
083: maxWeight = weight;
084: index = i;
085: toBreak = view;
086: breakX = x + span;
087: breakSpan = desiredSpan - span;
088: }
089: span += viewSpan;
090: }
091: if (index == -1) {
092: throw new Error(Messages.getString("swing.err.14")); //$NON-NLS-1$
093: }
094:
095: View broken = toBreak.breakView(fv.getFlowAxis(), toBreak
096: .getStartOffset(), breakX, breakSpan);
097: // Set parent of views to be removed from the row back to
098: // logical view
099: final View logicalView = getLogicalView(fv);
100: for (int i = index; i < row.getViewCount(); i++) {
101: row.getView(i).setParent(logicalView);
102: }
103: row.replace(index, row.getViewCount() - index,
104: new View[] { broken });
105: }
106:
107: protected int layoutRow(final FlowView fv, final int rowIndex,
108: final int pos) {
109: final View row = fv.getView(rowIndex);
110: final int flowAxis = fv.getFlowAxis();
111: final int flowStart = fv.getFlowStart(rowIndex);
112: final int flowSpan = fv.getFlowSpan(rowIndex);
113: int x = flowStart;
114: int rowSpan = flowSpan;
115: int span = 0;
116: int offset = pos;
117: int weight = BadBreakWeight;
118: int fix = 0;
119: View view;
120:
121: do {
122: rowSpan -= span;
123: x += span;
124:
125: view = createView(fv, offset, rowSpan, rowIndex);
126: if (view != null) {
127: span = (int) view.getPreferredSpan(flowAxis);
128: weight = view.getBreakWeight(flowAxis, offset,
129: rowSpan);
130: if (weight >= ForcedBreakWeight) {
131: final View broken = view.breakView(flowAxis,
132: offset, x, rowSpan);
133: if (view == broken && row.getViewCount() > 0) {
134: fix = 1;
135: break;
136: }
137: view = broken;
138: }
139: row.append(view);
140: offset = view.getEndOffset();
141: }
142: } while (view != null && span <= rowSpan
143: && weight < ForcedBreakWeight);
144:
145: if (span > rowSpan) {
146: adjustRow(fv, rowIndex, flowSpan, flowStart);
147: }
148:
149: return row.getEndOffset() + fix;
150: }
151:
152: protected View getLogicalView(final FlowView fv) {
153: return fv.layoutPool;
154: }
155:
156: protected View createView(final FlowView fv,
157: final int startOffset, final int spanLeft,
158: final int rowIndex) {
159: View logical = getLogicalView(fv);
160: int index = logical.getViewIndex(startOffset, Bias.Forward);
161: if (index == -1) {
162: return null;
163: }
164: View result = logical.getView(index);
165: if (startOffset != result.getStartOffset()) {
166: return result.createFragment(startOffset, result
167: .getEndOffset());
168: }
169: return result;
170: }
171:
172: static FlowStrategy getSharedStrategy() {
173: if (sharedStrategy == null) {
174: sharedStrategy = new FlowStrategy();
175: }
176:
177: return sharedStrategy;
178: }
179:
180: private void invalidateFlow(final FlowView fv,
181: final DocumentEvent event, final Rectangle alloc) {
182: if (event == null) {
183: fv.layoutChanged(X_AXIS);
184: fv.layoutChanged(Y_AXIS);
185: }
186:
187: if (alloc != null) {
188: Component container = fv.getComponent();
189: if (container != null) {
190: container.repaint(alloc.x, alloc.y, alloc.width,
191: alloc.height);
192: }
193: }
194: }
195:
196: }
197:
198: private static class LogicalView extends CompositeView {
199: private float spanX = -1;
200: private float spanY = -1;
201:
202: public LogicalView(final Element element) {
203: super (element);
204: }
205:
206: public int getResizeWeight(final int axis) {
207: return 1;
208: }
209:
210: public float getMinimumSpan(final int axis) {
211: return 0;
212: }
213:
214: public float getPreferredSpan(final int axis) {
215: if (axis == X_AXIS) {
216: if (spanX == -1) {
217: spanX = getSpanX();
218: }
219: return spanX;
220: }
221:
222: if (spanY == -1) {
223: spanY = getSpanY();
224: }
225: return spanY;
226: }
227:
228: public float getMaximumSpan(final int axis) {
229: return getPreferredSpan(axis);
230: }
231:
232: public void paint(final Graphics g, final Shape shape) {
233: throw new UnsupportedOperationException(Messages
234: .getString("swing.8B")); //$NON-NLS-1$
235: }
236:
237: public void preferenceChanged(final View child,
238: final boolean width, final boolean height) {
239: if (width) {
240: spanX = -1;
241: }
242: if (height) {
243: spanY = -1;
244: }
245: super .preferenceChanged(child, width, height);
246: }
247:
248: public AttributeSet getAttributes() {
249: final View parent = getParent();
250: return parent != null ? parent.getAttributes() : null;
251: }
252:
253: protected void loadChildren(final ViewFactory factory) {
254: if (factory != null) {
255: super .loadChildren(factory);
256: }
257: }
258:
259: protected void forwardUpdateToView(final View view,
260: final DocumentEvent event, final Shape shape,
261: final ViewFactory factory) {
262: view.setParent(this );
263: super .forwardUpdateToView(view, event, shape, factory);
264: }
265:
266: protected void childAllocation(final int index,
267: final Rectangle rc) {
268: }
269:
270: protected View getViewAtPoint(final int x, final int y,
271: final Rectangle alloc) {
272: throw new UnsupportedOperationException(Messages
273: .getString("swing.8B")); //$NON-NLS-1$
274: }
275:
276: protected int getViewIndexAtPosition(final int pos) {
277: if (pos < getStartOffset() || pos >= getEndOffset()) {
278: return -1;
279: }
280: return super .getViewIndexAtPosition(pos);
281: }
282:
283: protected boolean isAfter(final int x, final int y,
284: final Rectangle rc) {
285: throw new UnsupportedOperationException(Messages
286: .getString("swing.8B")); //$NON-NLS-1$
287: }
288:
289: protected boolean isBefore(final int x, final int y,
290: final Rectangle rc) {
291: throw new UnsupportedOperationException(Messages
292: .getString("swing.8B")); //$NON-NLS-1$
293: }
294:
295: private float getSpanX() {
296: float span = 0;
297: for (int i = 0; i < getViewCount(); i++) {
298: span += getView(i).getPreferredSpan(X_AXIS);
299: }
300: return span;
301: }
302:
303: private float getSpanY() {
304: float span = 0;
305: for (int i = 0; i < getViewCount(); i++) {
306: span = Math.max(span, getView(i).getPreferredSpan(
307: Y_AXIS));
308: }
309: return span;
310: }
311: }
312:
313: protected View layoutPool;
314: protected int layoutSpan = Short.MAX_VALUE;
315:
316: protected FlowStrategy strategy = FlowStrategy.getSharedStrategy();
317:
318: public FlowView(final Element element, final int axis) {
319: super (element, axis);
320: }
321:
322: protected abstract View createRow();
323:
324: public int getFlowAxis() {
325: return getAxis() == Y_AXIS ? X_AXIS : Y_AXIS;
326: }
327:
328: public int getFlowStart(final int rowIndex) {
329: return 0;
330: }
331:
332: public int getFlowSpan(final int rowIndex) {
333: return layoutSpan;
334: }
335:
336: public void insertUpdate(final DocumentEvent event,
337: final Shape alloc, final ViewFactory factory) {
338: layoutPool.insertUpdate(event, alloc, factory);
339: strategy.insertUpdate(this , event, shapeToRect(alloc));
340: }
341:
342: public void removeUpdate(final DocumentEvent event,
343: final Shape alloc, final ViewFactory factory) {
344: layoutPool.removeUpdate(event, alloc, factory);
345: strategy.removeUpdate(this , event, shapeToRect(alloc));
346: }
347:
348: public void changedUpdate(final DocumentEvent event,
349: final Shape alloc, final ViewFactory factory) {
350: layoutPool.changedUpdate(event, alloc, factory);
351: strategy.changedUpdate(this , event, shapeToRect(alloc));
352: }
353:
354: protected SizeRequirements calculateMinorAxisRequirements(
355: final int axis, final SizeRequirements sr) {
356: SizeRequirements result = sr != null ? sr
357: : new SizeRequirements();
358: result.minimum = (int) layoutPool.getMinimumSpan(axis);
359: result.preferred = (int) layoutPool.getPreferredSpan(axis);
360: result.maximum = Integer.MAX_VALUE;
361: result.alignment = ALIGN_CENTER;
362: return result;
363: }
364:
365: protected int getViewIndexAtPosition(final int pos) {
366: if (pos < getStartOffset() || pos >= getEndOffset()) {
367: return -1;
368: }
369: return super .getViewIndexAtPosition(pos);
370: }
371:
372: protected void layout(final int width, final int height) {
373: int span = getFlowAxis() == X_AXIS ? width : height;
374: if (span != layoutSpan) {
375: layoutSpan = span;
376: layoutChanged(X_AXIS);
377: layoutChanged(Y_AXIS);
378: }
379:
380: if (!isAllocationValid()) {
381: int h = (int) getPreferredSpan(Y_AXIS);
382: strategy.layout(this );
383: if (h != (int) getPreferredSpan(Y_AXIS)) {
384: preferenceChanged(null, false, true);
385: }
386: }
387:
388: super .layout(width, height);
389: }
390:
391: protected void loadChildren(final ViewFactory factory) {
392: if (layoutPool == null) {
393: createLogicalView();
394: strategy.insertUpdate(this , null, null);
395: } else if (layoutPool.getViewCount() == 0) {
396: ((CompositeView) layoutPool).loadChildren(getViewFactory());
397: strategy.insertUpdate(this , null, null);
398: }
399: }
400:
401: private void createLogicalView() {
402: if (layoutPool == null) {
403: layoutPool = new LogicalView(getElement());
404: layoutPool.setParent(this );
405: }
406: }
407:
408: private Rectangle shapeToRect(final Shape alloc) {
409: return alloc != null ? alloc.getBounds() : null;
410: }
411: }
|