001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.print.impl.provider;
042:
043: import java.awt.Color;
044: import java.awt.Font;
045: import java.awt.Graphics;
046: import java.awt.Graphics2D;
047:
048: import java.text.AttributedCharacterIterator;
049: import java.util.ArrayList;
050: import java.util.List;
051:
052: import javax.swing.JComponent;
053:
054: import org.netbeans.modules.print.impl.util.Option;
055: import static org.netbeans.modules.print.impl.util.UI.*;
056:
057: /**
058: * @author Vladimir Yaroslavskiy
059: * @version 2006.03.27
060: */
061: final class ComponentDocument extends JComponent {
062:
063: ComponentDocument(String text) {
064: init();
065: prepare(text);
066: perform();
067: }
068:
069: ComponentDocument(AttributedCharacterIterator[] iterators) {
070: init();
071: prepare(iterators);
072: perform();
073: }
074:
075: private void init() {
076: //out();
077: myWrapLines = Option.getDefault().isWrapLines();
078: myLineNumbers = Option.getDefault().isLineNumbers();
079: myTextColor = Option.getDefault().getTextColor();
080: myTextFont = Option.getDefault().getTextFont();
081: myBackgroundColor = Option.getDefault().getBackgroundColor();
082: myLineSpacing = Option.getDefault().getLineSpacing();
083: myLines = new ArrayList<ComponentLine>();
084: }
085:
086: private void prepare(String text) {
087: LineTokenizer stk = new LineTokenizer(text);
088:
089: while (stk.hasMoreTokens()) {
090: ComponentLine line = new ComponentLine(trimEnded(stk
091: .nextToken()), myTextFont, myTextColor);
092: //out();
093: //out(line.getWidth() + " '" + line + "'");
094: //line.show();
095: myLines.add(line);
096: }
097: }
098:
099: private void prepare(AttributedCharacterIterator[] iterators) {
100: for (AttributedCharacterIterator iterator : iterators) {
101: ComponentLine line = new ComponentLine(iterator,
102: myTextFont, myTextColor);
103: //out();
104: //out(line.getWidth() + " '" + line + "'");
105: //line.show();
106: myLines.add(line);
107: }
108: }
109:
110: private void perform() {
111: removeEmptyLinesAtTheEnd();
112:
113: if (myLineNumbers) {
114: prepareLineNumbering();
115: }
116: calculateOffset();
117:
118: if (myWrapLines) {
119: prepareWrapLines();
120: } else {
121: prepareNoWrapLines();
122: }
123: calculateMetrics();
124: }
125:
126: private void removeEmptyLinesAtTheEnd() {
127: int i = myLines.size() - 1;
128:
129: while (i >= 0) {
130: ComponentLine line = myLines.get(i--);
131:
132: if (line.isEmpty()) {
133: myLines.remove(line);
134: } else {
135: break;
136: }
137: }
138: }
139:
140: private String trimEnded(String value) {
141: int i = value.length() - 1;
142:
143: while (i >= 0 && value.charAt(i) == ' ') {
144: i--;
145: }
146: return value.substring(0, i + 1);
147: }
148:
149: private void prepareLineNumbering() {
150: int length = (myLines.size() + "").length(); // NOI18N
151: int number = 1;
152:
153: for (ComponentLine line : myLines) {
154: line.prepend(getNumber(number++, length));
155: }
156: }
157:
158: private void prepareNoWrapLines() {
159: //out();
160: int maxWidth = 0;
161:
162: for (ComponentLine line : myLines) {
163: int width = line.getWidth();
164:
165: if (width > maxWidth) {
166: maxWidth = width;
167: }
168: //out("" + maxWidth + " " + width + " '" + line + "'");
169: }
170: myWidth = maxWidth + myMinOffset;
171: //out();
172: //out(" WIDTH: " + myWidth);
173: }
174:
175: private void prepareWrapLines() {
176: myWidth = Option.getDefault().getPageWidth();
177: //out("Width: " + myWidth);
178: List<ComponentLine> lines = new ArrayList<ComponentLine>();
179:
180: for (ComponentLine line : myLines) {
181: //out(" see: " + line.getWidth() + " " + line);
182: if (line.getWidth() + myMinOffset <= myWidth) {
183: lines.add(line);
184: } else {
185: addWordWrappedLine(lines, line);
186: }
187: }
188: myLines = lines;
189: }
190:
191: private void addWordWrappedLine(List<ComponentLine> lines,
192: ComponentLine line) {
193: //out();
194: //out("add word wrap: '" + line);
195: if (line.getWidth() + myMinOffset <= myWidth) {
196: lines.add(line);
197: return;
198: }
199: int last = line.length();
200: ComponentLine part;
201: int k;
202:
203: while (true) {
204: //out(" while: '" + line + "' " + last);
205: k = line.lastIndexOf(' ', last - 1);
206:
207: if (k == -1) {
208: addCharWrappedLine(lines, line);
209: break;
210: }
211: last = k;
212: part = line.substring(0, k);
213: checkOffset(part);
214:
215: if (part.getWidth() + myMinOffset <= myWidth) {
216: //out(" -- '" + part + "' " + k);
217: if (part.isEmpty()) {
218: addCharWrappedLine(lines, line);
219: } else {
220: lines.add(part);
221: part = line.substring(k + 1);
222: checkOffset(part);
223: addWordWrappedLine(lines, part);
224: }
225: break;
226: }
227: }
228: }
229:
230: private void addCharWrappedLine(List<ComponentLine> lines,
231: ComponentLine line) {
232: if (line.getWidth() + myMinOffset <= myWidth) {
233: lines.add(line);
234: return;
235: }
236: ComponentLine part;
237: int k = line.length();
238:
239: while (k >= 0) {
240: part = line.substring(0, k);
241: checkOffset(part);
242:
243: if (part.getWidth() + myMinOffset <= myWidth) {
244: lines.add(part);
245: part = line.substring(k);
246: checkOffset(part);
247: addCharWrappedLine(lines, part);
248: break;
249: }
250: k--;
251: }
252: }
253:
254: private void checkOffset(ComponentLine line) {
255: int offset = -line.getOffset();
256:
257: if (offset > myMinOffset) {
258: myMinOffset = offset;
259: }
260: }
261:
262: private void calculateOffset() {
263: myMinOffset = 0;
264:
265: for (ComponentLine line : myLines) {
266: checkOffset(line);
267: }
268: //out("OFFSET: " + myMinOffset);
269: }
270:
271: private void calculateMetrics() {
272: myHeight = 0;
273:
274: int size = myLines.size();
275: myAscent = new int[size];
276: myDescent = new int[size];
277: myLeading = new int[size];
278: myCorrection = new int[size];
279:
280: int pageHeight = Option.getDefault().getPageHeight();
281: int breakPosition = pageHeight;
282: int prevPos;
283: //out();
284:
285: for (int i = 0; i < size; i++) {
286: ComponentLine line = myLines.get(i);
287:
288: myAscent[i] = (int) Math.round(line.getAscent()
289: * myLineSpacing);
290: myDescent[i] = line.getDescent();
291: myCorrection[i] = 0;
292: prevPos = myHeight;
293: //out(getAscent(line) + " " + getDescent(line) + " " + getLeading(line));
294:
295: myHeight += myAscent[i] + myDescent[i];
296:
297: if (myHeight > breakPosition && prevPos < breakPosition) {
298: myCorrection[i] = breakPosition - prevPos;
299: myHeight += myCorrection[i];
300: breakPosition += pageHeight;
301: }
302: if (i != size - 1) {
303: myLeading[i] = line.getLeading();
304: myHeight += myLeading[i];
305: }
306: }
307: //out("HEIGHT: " + myHeight);
308: }
309:
310: @Override
311: public int getWidth() {
312: return myWidth;
313: }
314:
315: @Override
316: public int getHeight() {
317: return myHeight;
318: }
319:
320: @Override
321: protected void paintComponent(Graphics graphics) {
322: Graphics2D g = Option.getDefault().getGraphics(graphics);
323:
324: g.setColor(myBackgroundColor);
325: g.fillRect(0, 0, myWidth, myHeight);
326:
327: int y = 0;
328:
329: for (int i = 0; i < myLines.size(); i++) {
330: ComponentLine line = myLines.get(i);
331: y += myCorrection[i] + myAscent[i];
332: line.draw(g, myMinOffset, y);
333:
334: y += myDescent[i] + myLeading[i];
335: }
336: }
337:
338: private String getNumber(int number, int length) {
339: StringBuffer buffer = new StringBuffer();
340: buffer.append(number);
341:
342: for (int i = buffer.length(); i < length; i++) {
343: buffer.insert(0, " "); // NOI18N
344: }
345: buffer.append(" "); // NOI18N
346:
347: return buffer.toString();
348: }
349:
350: // ---------------------------------------
351: private static final class LineTokenizer {
352:
353: public LineTokenizer(String value) {
354: myValue = value;
355: myLength = value.length();
356: myBuffer = new StringBuffer();
357: }
358:
359: public boolean hasMoreTokens() {
360: return myPos < myLength;
361: }
362:
363: public String nextToken() {
364: myBuffer.setLength(0);
365: String separator = "";
366: char c;
367:
368: while (myPos < myLength) {
369: c = myValue.charAt(myPos);
370: myPos++;
371:
372: if (c == '\r' || c == '\n') {
373: if (c == '\r' && myPos < myLength
374: && myValue.charAt(myPos) == '\n') {
375: myPos++;
376: }
377: break;
378: }
379: myBuffer.append(c);
380: }
381: return myBuffer.toString();
382: }
383:
384: private int myPos;
385: private int myLength;
386: private String myValue;
387: private StringBuffer myBuffer;
388: // { Unix - "\n", Windows - "\r\n", Mac - "\r" }
389: }
390:
391: private int myWidth;
392: private int myHeight;
393:
394: private int[] myAscent;
395: private int[] myDescent;
396: private int[] myLeading;
397: private int[] myCorrection;
398: private int myMinOffset;
399: private double myLineSpacing;
400:
401: private boolean myWrapLines;
402: private boolean myLineNumbers;
403:
404: private Font myTextFont;
405: private Color myTextColor;
406: private Color myBackgroundColor;
407:
408: private List<ComponentLine> myLines;
409: }
|