1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements.  See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership.  The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License.  You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied.  See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21 
22 package org.apache.openoffice.ooxml.viewer.tokenview;
23 
24 import java.awt.Color;
25 import java.awt.Container;
26 import java.awt.Font;
27 import java.awt.FontMetrics;
28 import java.awt.Graphics;
29 import java.awt.Graphics2D;
30 import java.awt.Point;
31 import java.awt.RenderingHints;
32 import java.awt.event.ComponentEvent;
33 import java.awt.event.ComponentListener;
34 import java.awt.event.MouseEvent;
35 import java.awt.event.MouseMotionListener;
36 import java.util.Iterator;
37 import java.util.Vector;
38 
39 import javax.swing.JPanel;
40 import javax.swing.JScrollPane;
41 import javax.swing.UIManager;
42 
43 /** A simple view of tokenized content.
44  *
45  *  Create the content by calling GetDocumentFactory() and using the returned
46  *  factory to add tokenized text.
47  */
48 @SuppressWarnings("serial")
49 public class TokenView<TokenType>
50     extends JPanel
51     implements MouseMotionListener, DocumentFactory.IRepaintTarget, ComponentListener
52 {
TokenView()53     public TokenView ()
54     {
55         maLines = new LineContainer<TokenType>();
56         maFormatter = new Formatter<TokenType>();
57 
58         addMouseMotionListener(this);
59         addComponentListener(this);
60     }
61 
62 
63 
64 
65     @Override
paintComponent(final Graphics aGraphics)66     public void paintComponent (final Graphics aGraphics)
67     {
68         super.paintComponent(aGraphics);
69 
70         final Graphics2D aG2 = (Graphics2D)aGraphics;
71         aG2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
72 
73         final FormatState<TokenType> aState;
74         synchronized(maLines)
75         {
76             aState = maFormatter.FormatText(aG2, maLines);
77         }
78         setPreferredSize(aState.GetTextBoundingSize());
79 
80         for (final Line<TokenType> aLine : aState)
81         {
82             PaintLineHighlight(aG2, aLine);
83             PaintLineNumber(aG2, aLine);
84 
85             int nX = mnTextStart;
86             for (final Run<TokenType> aRun : aLine)
87             {
88                 final Color aRunColor;
89                 if (aRun == maRunUnderMouse)
90                     aRunColor = maRunUnderMouseColor;
91                 else if (aRun == maHighlightedErrorRun)
92                     aRunColor = maErrorHighlightColor;
93                 else
94                     aRunColor = null;
95                 aRun.Paint(
96                     (Graphics2D)aGraphics,
97                     nX,
98                     aLine.GetBottom(),
99                     aRunColor);
100 
101                 nX += aRun.GetWidth();
102             }
103         }
104 
105         aGraphics.setColor(maSeparatorColor);
106 
107         final int nTop = aGraphics.getClipBounds().y;
108         final int nBottom = aGraphics.getClipBounds().y+aGraphics.getClipBounds().height;
109         aGraphics.drawLine(
110             mnBarPosition0,
111             nTop,
112             mnBarPosition0,
113             nBottom);
114         aGraphics.drawLine(
115             mnBarPosition1,
116             nTop,
117             mnBarPosition1,
118             nBottom);
119     }
120 
121 
122 
123 
124     /** Paint a line with a highlight.
125      *  There are different kinds of highlight:
126      *  - the current line
127      *  - one of three groups of enclosing parent elements
128      */
PaintLineHighlight( final Graphics2D aG2, final Line<TokenType> aLine)129     private void PaintLineHighlight (
130         final Graphics2D aG2,
131         final Line<TokenType> aLine)
132     {
133         final Color aBackgroundColor;
134         if (aLine == maHighlightedLine)
135             aBackgroundColor = maHighlightColor;
136         else
137             aBackgroundColor = maBackgroundColor;
138 
139         final Color aBarColor;
140         if (maLines.IsLineInGroup(aLine, maHighlightedGroup0))
141             aBarColor = maGroupHighlightColor0;
142         else if (maLines.IsLineInGroup(aLine, maHighlightedGroup1))
143             aBarColor = maGroupHighlightColor1;
144         else if (maLines.IsLineInGroup(aLine, maHighlightedGroup2))
145             aBarColor = maGroupHighlightColor2;
146         else
147             aBarColor = maBackgroundColor;
148 
149         aG2.setColor(aBarColor);
150         aG2.fillRect(
151             0,
152             aLine.GetTop(),
153             mnLeftBarWidth,
154             aLine.GetHeight());
155 
156         aG2.setColor(aBackgroundColor);
157         aG2.fillRect(
158             mnLeftBarWidth,
159             aLine.GetTop(),
160             getWidth() - mnLeftBarWidth,
161             aLine.GetHeight());
162     }
163 
164 
165 
166 
PaintLineNumber( final Graphics2D aG2, final Line<TokenType> aLine)167     private void PaintLineNumber (
168         final Graphics2D aG2,
169         final Line<TokenType> aLine)
170     {
171         final String sNumber = Integer.toString(aLine.GetStartOffset());
172         final FontMetrics aMetrics = aG2.getFontMetrics();
173         final int nWidth = aMetrics.stringWidth(sNumber);
174         final int nHeight = aMetrics.getHeight();
175 
176         aG2.setColor(maLineNumberColor);
177         aG2.setFont(maLineNumberFont);
178         aG2.drawString(
179             sNumber,
180             mnBarPosition0+1 + mnNumberBarWidth-nWidth,
181             aLine.GetBottom() - (aLine.GetHeight()-nHeight)/2 - aMetrics.getDescent());
182     }
183 
184 
185 
186 
187     @Override
mouseDragged(final MouseEvent aEvent)188     public void mouseDragged (final MouseEvent aEvent)
189     {
190     }
191 
192 
193 
194 
195     @Override
mouseMoved(final MouseEvent aEvent)196     public void mouseMoved (final MouseEvent aEvent)
197     {
198         final Line<TokenType> aLine = maLines.GetLineForY(aEvent.getY());
199         if (aLine != null)
200         {
201             UpdateHighlightedLine(aLine);
202             final Run<TokenType> aRun = aLine.GetRunForX(aEvent.getX() - mnTextStart);
203             SetRunUnderMouse(aRun);
204         }
205     }
206 
207 
208 
209 
UpdateHighlightedLine(final Line<TokenType> aLine)210     private void UpdateHighlightedLine (final Line<TokenType> aLine)
211     {
212         HighlightLine(aLine);
213 
214         final Iterator<Run<TokenType>> aRunIterator = aLine.iterator();
215         if (aRunIterator.hasNext())
216         {
217             final Run<TokenType> aRun = aRunIterator.next();
218             if (aRun.IsGroup())
219                 HighlightGroup(aRun);
220             else
221                 HighlightGroup(aRun.GetParent());
222         }
223     }
224 
225 
226 
227 
228     @Override
RequestRepaint()229     public void RequestRepaint()
230     {
231         repaint();
232     }
233 
234 
235 
236 
GetDocumentFactory()237     public DocumentFactory<TokenType> GetDocumentFactory()
238     {
239         return new DocumentFactory<TokenType>(maLines, this);
240     }
241 
242 
243 
244 
HighlightLine(final Line<TokenType> aLine)245     private void HighlightLine (final Line<TokenType> aLine)
246     {
247         if (aLine != maHighlightedLine)
248         {
249             maHighlightedLine = aLine;
250             repaint();
251         }
252     }
253 
254 
255 
256 
HighlightGroup(final Run<TokenType> aRun)257     private void HighlightGroup (final Run<TokenType> aRun)
258     {
259         if (maHighlightedGroup0 != aRun)
260         {
261             maHighlightedGroup0 = aRun;
262 
263             if (aRun != null)
264             {
265                 final Run<TokenType> aGroup1 = aRun.GetParent();
266                 maHighlightedGroup1 = aGroup1;
267 
268                 if (aGroup1 != null)
269                 {
270                     final Run<TokenType> aGroup2 = aGroup1.GetParent();
271                     maHighlightedGroup2 = aGroup2;
272                 }
273             }
274             repaint();
275         }
276     }
277 
278 
279 
280 
281     @Override
componentHidden(ComponentEvent e)282     public void componentHidden(ComponentEvent e)
283     {
284     }
285 
286 
287 
288 
289     @Override
componentMoved(final ComponentEvent aEvent)290     public void componentMoved (final ComponentEvent aEvent)
291     {
292         final Point aPoint = getMousePosition();
293         if (aPoint != null)
294             UpdateHighlightedLine(maLines.GetLineForY(aPoint.y));
295     }
296 
297 
298 
299 
300     @Override
componentResized(ComponentEvent e)301     public void componentResized(ComponentEvent e)
302     {
303     }
304 
305 
306 
307 
308     @Override
componentShown(ComponentEvent e)309     public void componentShown(ComponentEvent e)
310     {
311     }
312 
313 
314 
315 
GetRun(final int nOffset)316     public Run<TokenType> GetRun (final int nOffset)
317     {
318         final Line<TokenType> aLine = maLines.GetLineForOffset(nOffset);
319         if (aLine != null)
320             return aLine.GetRunForOffset(nOffset);
321         else
322             return null;
323     }
324 
325 
326 
327 
328     /** Return all runs that completely or partially lie in the range from
329      *  start offset (including) and end offset (excluding).
330      */
GetRuns(final int nStartOffset, final int nEndOffset)331     public RunRange<TokenType> GetRuns (final int nStartOffset, final int nEndOffset)
332     {
333         final Vector<Run<TokenType>> aRuns = new Vector<>();
334 
335         for (final Line<TokenType> aLine : maLines.GetLinesForOffsets(nStartOffset, nEndOffset))
336             for (final Run<TokenType> aRun : aLine.GetRunsForOffsets(nStartOffset, nEndOffset))
337                 aRuns.add(aRun);
338 
339         return new RunRange<TokenType>(aRuns);
340     }
341 
342 
343 
344 
MarkError(final Run<TokenType> aRun)345     public void MarkError (final Run<TokenType> aRun)
346     {
347         maHighlightedErrorRun = aRun;
348         repaint();
349     }
350 
351 
352 
353 
ShowRun(final Run<TokenType> aRun)354     public void ShowRun (final Run<TokenType> aRun)
355     {
356         final Container aComponent = getParent().getParent();
357         if (aComponent instanceof JScrollPane)
358             ((JScrollPane)aComponent).getVerticalScrollBar().setValue(
359                     aRun.GetLine().GetTop());
360     }
361 
362 
363 
364 
SetRunUnderMouse(final Run<TokenType> aRun)365     private void SetRunUnderMouse (final Run<TokenType> aRun)
366     {
367         if (maRunUnderMouse != aRun)
368         {
369             maRunUnderMouse = aRun;
370             if (maRunUnderMouse != null)
371                 setToolTipText(maRunUnderMouse.GetToolTipText());
372             else
373                 setToolTipText(null);
374             repaint();
375         }
376     }
377 
378 
379 
380 
381     private final LineContainer<TokenType> maLines;
382     private final Formatter<TokenType> maFormatter;
383     private Line<TokenType> maHighlightedLine;
384     private Run<TokenType> maHighlightedGroup0;
385     private Run<TokenType> maHighlightedGroup1;
386     private Run<TokenType> maHighlightedGroup2;
387     private Run<TokenType> maHighlightedErrorRun;
388     private Run<TokenType> maRunUnderMouse;
389 
390     private final static int mnLeftBarWidth = 10;
391     private final static int mnNumberBarWidth = 30;
392     private final static int mnBarPosition0 = mnLeftBarWidth;
393     private final static int mnBarPosition1 = mnBarPosition0 + mnNumberBarWidth ;
394     private final static int mnTextStart = mnBarPosition1 + 2;
395     private final static Color maSeparatorColor = Color.GRAY;
396     private final static Color maBackgroundColor = Color.WHITE;
397     private final static Color maHighlightColor = new Color(0xB0E0E6); // Powder Blue
398     private final static Color maGroupHighlightColor0 = new Color(0x32CD32); // Lime Green
399     private final static Color maGroupHighlightColor1 = new Color(0x90EE90); // Light Green
400     private final static Color maGroupHighlightColor2 = new Color(0xbbFfbb);
401     private final static Color maErrorHighlightColor = new Color(0xff3020);
402     private final static Color maLineNumberColor = new Color(0x808080);
403     private final static Color maRunUnderMouseColor = maGroupHighlightColor2;
404     private final static Font maLineNumberFont = UIManager.getDefaults().getFont("TextField.font").deriveFont(9.0f);
405 
406 }
407