Source code: com/virtuosotechnologies/asaph/standardgui/PrintRenderHelper.java
1 /*
2 ================================================================================
3
4 FILE: PrintRenderHelper.java
5
6 PROJECT:
7
8 Asaph
9
10 CONTENTS:
11
12 Helper that does layout and rendering of the song for printing
13
14 PROGRAMMERS:
15
16 Daniel Azuma (DA) <dazuma@kagi.com>
17
18 COPYRIGHT:
19
20 Copyright (C) 2003 Daniel Azuma (dazuma@kagi.com)
21
22 This program is free software; you can redistribute it and/or
23 modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2
25 of the License, or (at your option) any later version.
26
27 This program is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public
33 License along with this program; if not, write to
34 Free Software Foundation, Inc.
35 59 Temple Place, Suite 330
36 Boston, MA 02111-1307 USA
37
38 ================================================================================
39 */
40
41
42 package com.virtuosotechnologies.asaph.standardgui;
43
44
45 import java.util.List;
46 import java.util.ArrayList;
47 import java.util.Iterator;
48 import java.awt.Graphics2D;
49 import java.awt.Color;
50
51 import virtuoso.asaph.util.render.BodyLayout;
52
53
54 /**
55 * Helper that does layout and rendering of the song for printing
56 */
57 /*package*/ class PrintRenderHelper
58 {
59 private static final float EPSILON = 1;
60
61
62 private float imageableWidth_;
63 private float imageableHeight_;
64 private int numColumns_;
65 private float unscaledGutterWidth_;
66 private float gutterWidth_;
67 private float columnWidth_;
68 private RenderHelper fullRender_;
69 private float scale_;
70 private List pages_;
71 private List columnPopulations_;
72
73
74 private static class BlockLayout
75 {
76 private int column;
77 private float yPosition;
78 private BodyLayout layout;
79
80 private BlockLayout(
81 int c,
82 float y,
83 BodyLayout lo)
84 {
85 column = c;
86 yPosition = y;
87 layout = lo;
88 }
89 }
90
91
92 private static class PageLayout
93 {
94 private List blocks = new ArrayList();
95 private float height = 0;
96 private float footerPos = 0;
97 }
98
99
100 /*package*/ PrintRenderHelper(
101 RenderHelper renderHelper,
102 float imageableWidth,
103 float imageableHeight,
104 int columns,
105 float gutter)
106 {
107 this(renderHelper, imageableWidth, imageableHeight, columns, gutter, null);
108 }
109
110
111 private PrintRenderHelper(
112 RenderHelper renderHelper,
113 float imageableWidth,
114 float imageableHeight,
115 int columns,
116 float gutter,
117 List columnPopulations)
118 {
119 imageableWidth_ = imageableWidth;
120 imageableHeight_ = imageableHeight;
121 numColumns_ = columns;
122 gutterWidth_ = gutter;
123 unscaledGutterWidth_ = gutter;
124 columnWidth_ = (imageableWidth-(numColumns_-1)*gutterWidth_)/columns;
125 fullRender_ = renderHelper;
126 if (columnPopulations != null)
127 {
128 columnPopulations_ = columnPopulations;
129 }
130 else
131 {
132 columnPopulations_ = new ArrayList();
133 }
134
135 // Make sure song fits into column horizontally
136 scale_ = 1;
137 if (columnWidth_ < fullRender_.getWidth())
138 {
139 if (columnWidth_ < fullRender_.getMinWidth())
140 {
141 float width = fullRender_.getMinWidth();
142 scale_ = columnWidth_/width;
143 columnWidth_ = width;
144 }
145 fullRender_ = fullRender_.createUsingWidth(columnWidth_);
146 }
147 else
148 {
149 columnWidth_ = fullRender_.getWidth();
150 }
151 BodyLayout fullBody = fullRender_.getBodyLayout();
152 int numBlocks = fullBody.getBlockCount();
153
154 // Make sure blocks fit vertically
155 for (int i=0; i<numBlocks; ++i)
156 {
157 BodyLayout block = fullBody.getNthBlockLayout(i);
158 float h = block.getHeight();
159 if (h+EPSILON > imageableHeight/scale_)
160 {
161 scale_ = imageableHeight/(h+EPSILON);
162 }
163 }
164
165 // Now we know the scale
166 float scaledImageableWidth = imageableWidth/scale_;
167 float scaledImageableHeight = imageableHeight/scale_;
168
169 // Start arranging pages
170 pages_ = new ArrayList();
171 PageLayout curPage = new PageLayout();
172 float curY = fullRender_.getHeaderHeight();
173 pages_.add(curPage);
174 int curBlocksOnColumn = 0;
175 int curColumn = 0;
176 int pageColumn = 0;
177
178 // Arrange each block, plus footer
179 int imax = fullRender_.hasFooter() ? numBlocks+1 : numBlocks;
180 for (int i=0; i<imax; ++i)
181 {
182 // Calc blockHeight, and initial guess at y position and new column height,
183 // assuming the block shows up on the current column
184 float y;
185 if (i == numBlocks)
186 {
187 y = curY + fullRender_.getFooterSeparation();
188 }
189 else if (i == 0)
190 {
191 y = curY + fullRender_.getHeaderSeparation();
192 }
193 else
194 {
195 y = curY + fullRender_.getBlockSpacing();
196 }
197 float blockHeight;
198 if (i == numBlocks)
199 {
200 blockHeight = fullRender_.getFooterHeight();
201 }
202 else
203 {
204 blockHeight = fullBody.getNthBlockLayout(i).getHeight();
205 }
206 float nh = y + blockHeight;
207
208 // Determine if the block should go on the next column.
209 boolean needsNewColumn;
210 if (columnPopulations != null)
211 {
212 needsNewColumn = (curBlocksOnColumn ==
213 ((Integer)columnPopulations.get(curColumn)).intValue());
214 }
215 else
216 {
217 needsNewColumn = nh > scaledImageableHeight;
218 }
219
220 // Create new column if necessary
221 if (needsNewColumn)
222 {
223 if (pages_.size() == 1 && pageColumn > 0)
224 {
225 // On the first page, if the column is not the first column, attempt
226 // to move it down to start after the header, if there's room.
227 float delta = Math.min(scaledImageableHeight-curY,
228 fullRender_.getHeaderHeight()+fullRender_.getHeaderSeparation());
229 for (Iterator iter = curPage.blocks.iterator(); iter.hasNext(); )
230 {
231 BlockLayout block = (BlockLayout)iter.next();
232 if (block.column == pageColumn)
233 {
234 block.yPosition += delta;
235 }
236 }
237 curY += delta;
238 if (curY > curPage.height)
239 {
240 curPage.height = curY;
241 }
242 }
243 ++pageColumn;
244 if (pageColumn == numColumns_)
245 {
246 curPage = new PageLayout();
247 pages_.add(curPage);
248 pageColumn = 0;
249 }
250 y = 0;
251 nh = blockHeight;
252 if (columnPopulations == null)
253 {
254 columnPopulations_.add(new Integer(curBlocksOnColumn));
255 }
256 curBlocksOnColumn = 0;
257 ++curColumn;
258 }
259
260 // Add block
261 if (i == numBlocks)
262 {
263 curPage.footerPos = y;
264 }
265 else
266 {
267 curPage.blocks.add(new BlockLayout(pageColumn, y,
268 fullBody.getNthBlockLayout(i)));
269 }
270 curY = nh;
271 if (curY > curPage.height)
272 {
273 curPage.height = curY;
274 }
275 ++curBlocksOnColumn;
276 }
277 if (pages_.size() == 1 && pageColumn > 0)
278 {
279 // On the first page, if the column is not the first column, attempt
280 // to move it down to start after the header, if there's room.
281 float delta = Math.min(scaledImageableHeight-curY,
282 fullRender_.getHeaderHeight()+fullRender_.getHeaderSeparation());
283 for (Iterator iter = curPage.blocks.iterator(); iter.hasNext(); )
284 {
285 BlockLayout block = (BlockLayout)iter.next();
286 if (block.column == pageColumn)
287 {
288 block.yPosition += delta;
289 }
290 }
291 curY += delta;
292 if (curY > curPage.height)
293 {
294 curPage.height = curY;
295 }
296 }
297
298 // Finish
299 if (columnPopulations == null)
300 {
301 // Record population of last column
302 columnPopulations_.add(new Integer(curBlocksOnColumn));
303 }
304 else
305 {
306 // adjust the scale if necessary
307 float maxHeight = 0;
308 for (Iterator iter = pages_.iterator(); iter.hasNext(); )
309 {
310 curPage = (PageLayout)iter.next();
311 if (curPage.height > maxHeight)
312 {
313 maxHeight = curPage.height;
314 }
315 }
316 if (maxHeight > scaledImageableHeight)
317 {
318 scale_ = imageableHeight/maxHeight;
319 }
320 }
321 }
322
323
324 /*package*/ PrintRenderHelper createUsingGraphics(
325 Graphics2D g2d)
326 {
327 RenderHelper newFullRender = fullRender_.createUsingGraphics(g2d);
328 return new PrintRenderHelper(newFullRender, imageableWidth_, imageableHeight_,
329 numColumns_, unscaledGutterWidth_, columnPopulations_);
330 }
331
332
333 /*package*/ int getNumPages()
334 {
335 return pages_.size();
336 }
337
338
339 /*package*/ float getRequiredScale()
340 {
341 return scale_;
342 }
343
344
345 /*package*/ int getNumColumns()
346 {
347 return numColumns_;
348 }
349
350
351 /*package*/ float getColumnWidth()
352 {
353 return columnWidth_;
354 }
355
356
357 /*package*/ float getGutterWidth()
358 {
359 return gutterWidth_;
360 }
361
362
363 /*package*/ float getWidth()
364 {
365 return (columnWidth_+gutterWidth_)*numColumns_-gutterWidth_;
366 }
367
368
369 /*package*/ float getHeightForPage(
370 int pageNum)
371 {
372 return ((PageLayout)pages_.get(pageNum)).height;
373 }
374
375
376 /*package*/ void paintPage(
377 int pageNum,
378 Graphics2D g2d,
379 float x,
380 float y)
381 {
382 RenderHelper.mungeGraphics2D(g2d);
383 g2d.setColor(Color.black);
384 PageLayout pageLayout = (PageLayout)pages_.get(pageNum);
385 if (pageNum == 0)
386 {
387 fullRender_.paintHeader(g2d, x, y);
388 }
389 if (pageLayout.footerPos != 0)
390 {
391 fullRender_.paintFooter(g2d, x, y+pageLayout.footerPos);
392 }
393 for (Iterator iter = pageLayout.blocks.iterator(); iter.hasNext(); )
394 {
395 BlockLayout blockLayout = (BlockLayout)iter.next();
396 fullRender_.paintBlock(blockLayout.layout, g2d,
397 x+(columnWidth_+gutterWidth_)*blockLayout.column,
398 y+blockLayout.yPosition);
399 }
400 }
401 }