001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance.text;
031:
032: import java.awt.*;
033:
034: import javax.swing.JComponent;
035: import javax.swing.text.*;
036:
037: import org.jvnet.substance.SubstanceLookAndFeel;
038: import org.jvnet.substance.painter.text.SubstanceTextPainter;
039: import org.jvnet.substance.theme.SubstanceTheme;
040: import org.jvnet.substance.utils.*;
041:
042: /**
043: * Text utilities for painting views of text components with
044: * {@link SubstanceTextPainter}-based approach.
045: *
046: * @author Kirill Grouchnikov
047: */
048: public class TextUtilities {
049: /**
050: * Gets the view component.
051: *
052: * @param view
053: * Text view.
054: * @return View component.
055: */
056: private static JComponent getJComponent(View view) {
057: if (view != null) {
058: Component component = view.getContainer();
059: if (component instanceof JComponent) {
060: return (JComponent) component;
061: }
062: }
063: return null;
064: }
065:
066: /**
067: * Draws the specified text segment.
068: *
069: * @param view
070: * Text view.
071: * @param s
072: * Text segment.
073: * @param x
074: * X coordinate for the text painting.
075: * @param y
076: * Y coordinate for the text painting.
077: * @param g
078: * Graphic context.
079: * @param e
080: * Tab expander.
081: * @param startOffset
082: * The starting offset.
083: * @return X coordinate for drawing the next segment.
084: */
085: public static int drawTabbedText(View view, Segment s, int x,
086: int y, Graphics g, TabExpander e, int startOffset) {
087: JComponent component = getJComponent(view);
088: FontMetrics metrics = g.getFontMetrics();
089: int nextX = x;
090: char[] txt = s.array;
091: int txtOffset = s.offset;
092: int flushLen = 0;
093: int flushIndex = s.offset;
094: int spaceAddon = 0;
095: int spaceAddonLeftoverEnd = -1;
096: int startJustifiableContent = 0;
097: int endJustifiableContent = 0;
098: int n = s.offset + s.count;
099:
100: for (int i = txtOffset; i < n; i++) {
101: if (txt[i] == '\t'
102: || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd)
103: && (txt[i] == ' ')
104: && startJustifiableContent <= i && i <= endJustifiableContent)) {
105: if (flushLen > 0) {
106: nextX = drawChars(component, g, txt, flushIndex,
107: flushLen, x, y);
108: flushLen = 0;
109: }
110: flushIndex = i + 1;
111: if (txt[i] == '\t') {
112: if (e != null) {
113: nextX = (int) e.nextTabStop(nextX, startOffset
114: + i - txtOffset);
115: } else {
116: nextX += metrics.charWidth(' ');
117: }
118: } else if (txt[i] == ' ') {
119: nextX += metrics.charWidth(' ') + spaceAddon;
120: if (i <= spaceAddonLeftoverEnd) {
121: nextX++;
122: }
123: }
124: x = nextX;
125: } else if ((txt[i] == '\n') || (txt[i] == '\r')) {
126: if (flushLen > 0) {
127: nextX = drawChars(component, g, txt, flushIndex,
128: flushLen, x, y);
129: flushLen = 0;
130: }
131: flushIndex = i + 1;
132: x = nextX;
133: } else {
134: flushLen += 1;
135: }
136: }
137: if (flushLen > 0) {
138: nextX = drawChars(component, g, txt, flushIndex, flushLen,
139: x, y);
140: }
141:
142: // SubstanceTextPainter textPainter = SubstanceLookAndFeel
143: // .getCurrentTextPainter();
144: // if (textPainter.needsBackgroundImage()) {
145: // textPainter.setBackgroundImage(component, null, component
146: // .getBackground(), false, 0, 0, null);
147: // }
148: // Graphics2D g2d = (Graphics2D) g.create();
149: // textPainter.paintText(g, component, textLines);
150: // g2d.dispose();
151: return nextX;
152: }
153:
154: /**
155: * Draws the specified text characters.
156: *
157: * @param c
158: * Component.
159: * @param g
160: * Graphic context.
161: * @param data
162: * Characters to draw.
163: * @param offset
164: * Offset into the characters array.
165: * @param length
166: * Number of characters to draw.
167: * @param x
168: * X coordinate for the text painting.
169: * @param y
170: * Y coordinate for the text painting.
171: * @return X coordinate for drawing the next segment.
172: */
173: public static int drawChars(JComponent c, Graphics g, char[] data,
174: int offset, int length, int x, int y) {
175: if (length <= 0) { // no need to paint empty strings
176: return x;
177: }
178: FontMetrics fm = g.getFontMetrics();
179: int nextX = x
180: + g.getFontMetrics().charsWidth(data, offset, length);
181: SubstanceTextPainter textPainter = SubstanceLookAndFeel
182: .getCurrentTextPainter();
183: textPainter.attachText(c, new Rectangle(x, y - fm.getAscent(),
184: nextX - x, fm.getHeight()), new String(data, offset,
185: length), -1, g.getFont(), g.getColor(), null);
186: return nextX;
187: }
188:
189: public static int drawSelectedText(Graphics g, View view,
190: TabExpander tabExpander, int x, int y, int p0, int p1)
191: throws BadLocationException {
192: JTextComponent host = (JTextComponent) view.getContainer();
193: Highlighter h = host.getHighlighter();
194: g.setFont(host.getFont());
195: Caret c = host.getCaret();
196: ComponentState hostState = host.isEnabled() ? ComponentState.DEFAULT
197: : ComponentState.DISABLED_UNSELECTED;
198: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(host,
199: hostState);
200: Color color = theme.getForegroundColor();
201: if (c.isSelectionVisible() && h != null) {
202: color = theme.getSelectionForegroundColor();
203: }
204: g.setColor(color);
205: Document doc = view.getDocument();
206: Segment s = new Segment();
207: doc.getText(p0, p1 - p0, s);
208: int ret = TextUtilities.drawTabbedText(view, s, x, y, g,
209: tabExpander, p0);
210: // System.out.println("\tSelected " + p0 + "-" + p1);
211: return ret;
212: }
213:
214: public static int drawUnselectedText(Graphics g, View view,
215: TabExpander tabExpander, int x, int y, int p0, int p1)
216: throws BadLocationException {
217: JTextComponent host = (JTextComponent) view.getContainer();
218: ComponentState hostState = host.isEnabled() ? ComponentState.DEFAULT
219: : ComponentState.DISABLED_UNSELECTED;
220: Color unselected = SubstanceCoreUtilities.getForegroundColor(
221: host, hostState, hostState);
222: g.setColor(unselected);
223: Document doc = view.getDocument();
224: Segment s = new Segment();
225: doc.getText(p0, p1 - p0, s);
226: int ret = TextUtilities.drawTabbedText(view, s, x, y, g,
227: tabExpander, p0);
228: // System.out.println("\tUnselected " + p0 + "-" + p1);
229: return ret;
230: }
231:
232: }
|