1 /* ============================================================
2  * JRobin : Pure java implementation of RRDTool's functionality
3  * ============================================================
4  *
5  * Project Info:  http://www.jrobin.org
6  * Project Lead:  Sasa Markovic (saxon@jrobin.org)
7  *
8  * Developers:    Sasa Markovic (saxon@jrobin.org)
9  *
10  *
11  * (C) Copyright 2003-2005, by Sasa Markovic.
12  *
13  * This library is free software; you can redistribute it and/or modify it under the terms
14  * of the GNU Lesser General Public License as published by the Free Software Foundation;
15  * either version 2.1 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19  * See the GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License along with this
22  * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */

25 package org.jrobin.graph;
26
27 import java.util.ArrayList;
28 import java.util.List;
29
30 class LegendComposer implements RrdGraphConstants {
31     private RrdGraphDef gdef;
32     private ImageWorker worker;
33     private int legX, legY, legWidth;
34     private double interlegendSpace;
35     private double leading;
36     private double smallLeading;
37     private double boxSpace;
38
39     LegendComposer(RrdGraph rrdGraph, int legX, int legY, int legWidth) {
40         this.gdef = rrdGraph.gdef;
41         this.worker = rrdGraph.worker;
42         this.legX = legX;
43         this.legY = legY;
44         this.legWidth = legWidth;
45         interlegendSpace = rrdGraph.getInterlegendSpace();
46         leading = rrdGraph.getLeading();
47         smallLeading = rrdGraph.getSmallLeading();
48         boxSpace = rrdGraph.getBoxSpace();
49     }
50
51     int placeComments() {
52         Line line = new Line();
53         for (CommentText comment : gdef.comments) {
54             if (comment.isValidGraphElement()) {
55                 if (!line.canAccomodate(comment)) {
56                     line.layoutAndAdvance(false);
57                     line.clear();
58                 }
59                 line.add(comment);
60             }
61         }
62         line.layoutAndAdvance(true);
63         worker.dispose();
64         return legY;
65     }
66
67     class Line {
68         private String lastMarker;
69         private double width;
70         private int spaceCount;
71         private boolean noJustification;
72         private List<CommentText> comments = new ArrayList<CommentText>();
73
74         Line() {
75             clear();
76         }
77
78         void clear() {
79             lastMarker = "";
80             width = 0;
81             spaceCount = 0;
82             noJustification = false;
83             comments.clear();
84         }
85
86         boolean canAccomodate(CommentText comment) {
87             // always accommodate if empty
88             if (comments.size() == 0) {
89                 return true;
90             }
91             // cannot accommodate if the last marker was \j, \l, \r, \c, \s
92             if (lastMarker.equals(ALIGN_LEFT_MARKER) || lastMarker.equals(ALIGN_CENTER_MARKER) ||
93                     lastMarker.equals(ALIGN_RIGHT_MARKER) || lastMarker.equals(ALIGN_JUSTIFIED_MARKER) ||
94                     lastMarker.equals(VERTICAL_SPACING_MARKER)) {
95                 return false;
96             }
97             // cannot accommodate if line would be too long
98             double commentWidth = getCommentWidth(comment);
99             if (!lastMarker.equals(GLUE_MARKER)) {
100                 commentWidth += interlegendSpace;
101             }
102             return width + commentWidth <= legWidth;
103         }
104
105         void add(CommentText comment) {
106             double commentWidth = getCommentWidth(comment);
107             if (comments.size() > 0 && !lastMarker.equals(GLUE_MARKER)) {
108                 commentWidth += interlegendSpace;
109                 spaceCount++;
110             }
111             width += commentWidth;
112             lastMarker = comment.marker;
113             noJustification |= lastMarker.equals(NO_JUSTIFICATION_MARKER);
114             comments.add(comment);
115         }
116
117         void layoutAndAdvance(boolean isLastLine) {
118             if (comments.size() > 0) {
119                 if (lastMarker.equals(ALIGN_LEFT_MARKER)) {
120                     placeComments(legX, interlegendSpace);
121                 }
122                 else if (lastMarker.equals(ALIGN_RIGHT_MARKER)) {
123                     placeComments(legX + legWidth - width, interlegendSpace);
124                 }
125                 else if (lastMarker.equals(ALIGN_CENTER_MARKER)) {
126                     placeComments(legX + (legWidth - width) / 2.0, interlegendSpace);
127                 }
128                 else if (lastMarker.equals(ALIGN_JUSTIFIED_MARKER)) {
129                     // anything to justify?
130                     if (spaceCount > 0) {
131                         placeComments(legX, (legWidth - width) / spaceCount + interlegendSpace);
132                     }
133                     else {
134                         placeComments(legX, interlegendSpace);
135                     }
136                 }
137                 else if (lastMarker.equals(VERTICAL_SPACING_MARKER)) {
138                     placeComments(legX, interlegendSpace);
139                 }
140                 else {
141                     // nothing specified, align with respect to '\J'
142                     if (noJustification || isLastLine) {
143                         placeComments(legX, interlegendSpace);
144                     }
145                     else {
146                         placeComments(legX, (legWidth - width) / spaceCount + interlegendSpace);
147                     }
148                 }
149                 if (lastMarker.equals(VERTICAL_SPACING_MARKER)) {
150                     legY += smallLeading;
151                 }
152                 else {
153                     legY += leading;
154                 }
155             }
156         }
157
158         private double getCommentWidth(CommentText comment) {
159             double commentWidth = worker.getStringWidth(comment.resolvedText, gdef.smallFont);
160             if (comment instanceof LegendText) {
161                 commentWidth += boxSpace;
162             }
163             return commentWidth;
164         }
165
166         private void placeComments(double xStart, double space) {
167             double x = xStart;
168             for (CommentText comment : comments) {
169                 comment.x = (int) x;
170                 comment.y = legY;
171                 x += getCommentWidth(comment);
172                 if (!comment.marker.equals(GLUE_MARKER)) {
173                     x += space;
174                 }
175             }
176         }
177     }
178 }
179