001: package jimm.datavision.layout.swing;
002:
003: import jimm.datavision.*;
004: import jimm.datavision.field.*;
005: import jimm.datavision.layout.LayoutEngine;
006: import jimm.datavision.gui.ExportWin;
007: import jimm.datavision.gui.MenuUtils;
008: import jimm.datavision.gui.StatusDialog;
009: import jimm.util.I18N;
010: import java.awt.*;
011: import java.awt.print.*;
012: import java.awt.event.*;
013: import javax.swing.*;
014: import java.net.URL;
015: import java.util.ArrayList;
016:
017: /**
018: * <code>SwingLE</code> is a layout engine that creates a Swing window.
019: * The window can be printed by selecting the appropriate menu item.
020: *
021: * @author Jim Menard, <a href="mailto:jimm@io.com">jimm@io.com</a>
022: * @see SwingPageContents
023: * @see SwingPage
024: * @see SwingField
025: * @see SwingPrintBook
026: */
027: public class SwingLE extends LayoutEngine {
028:
029: protected static final Dimension WINDOW_START_SIZE = new Dimension(
030: 600, 400);
031: protected static final String PRINT_ICON = "images/Print16.gif";
032: protected static final String EXPORT_ICON = "images/Export16.gif";
033: protected static final String FIRST_ICON = "images/Home16.gif";
034: protected static final String PREV_ICON = "images/Back16.gif";
035: protected static final String NEXT_ICON = "images/Forward16.gif";
036: protected static final String LAST_ICON = "images/Down16.gif";
037:
038: protected JFrame frame;
039: protected Dimension pageDim;
040: protected JPanel cardPanel;
041: protected JScrollPane scroller;
042: protected int displayPageNum; // Starts at 1
043: protected ArrayList pageContents;
044: protected SwingPageContents pageBeingBuilt;
045: protected JLabel pageCountLabel;
046: protected Action printAction;
047: protected Action closeAction;
048: protected Action exportAction;
049: protected Action goFirstAction;
050: protected Action goPrevAction;
051: protected Action goNextAction;
052: protected Action goLastAction;
053:
054: /**
055: * Constructor.
056: */
057: public SwingLE() {
058: super ();
059: pageContents = new ArrayList();
060: }
061:
062: public void cancel() {
063: super .cancel();
064: close();
065: }
066:
067: public JFrame getJFrame() {
068: return frame;
069: }
070:
071: /**
072: * Creates window and displays a blank page.
073: */
074: protected void doStart() {
075: pageDim = new Dimension((int) pageWidth(), (int) pageHeight());
076: frame = new JFrame(report.getTitle());
077: makeActions();
078: makeMenu(frame);
079: frame.getContentPane().add(makeToolbar(), BorderLayout.NORTH);
080:
081: // Card panel for displaying pages
082: cardPanel = new JPanel();
083: CardLayout cardLayout = new CardLayout(0, 0);
084: cardPanel.setLayout(cardLayout);
085:
086: // Set sizes of scroller (window size) and card panel (page size)
087: cardPanel.setPreferredSize(pageDim);
088:
089: // New, blank, dummy card we can display until the first page is
090: // generated.
091: JPanel blankPage = new JPanel();
092: blankPage.setBackground(Color.white);
093: blankPage.setPreferredSize(pageDim);
094: cardPanel.add(blankPage, "blank page");
095: cardLayout.show(cardPanel, "blank page");
096:
097: // Scroller containing the card panel
098: scroller = new JScrollPane(cardPanel);
099: scroller.setPreferredSize(WINDOW_START_SIZE);
100: frame.getContentPane().add(scroller, BorderLayout.CENTER);
101:
102: displayPageNum = 0;
103:
104: frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
105: frame.addWindowListener(new WindowAdapter() {
106: public void windowClosing(WindowEvent e) {
107: close();
108: }
109: });
110:
111: frame.pack();
112: frame.setVisible(true);
113: }
114:
115: /**
116: * Done loading report.
117: */
118: protected void doEnd() {
119: printAction.setEnabled(true);
120:
121: if (pageContents.size() > 1) // Start prebuilding the report's last page
122: pageBeingBuilt.prebuildPage();
123:
124: // Calling displayPage() will force page 1 to finish building and
125: // will display it.
126: if (displayPageNum == 0)
127: displayPage(1);
128: }
129:
130: /**
131: * Creates the actions used by menu items and toolbar widgets.
132: */
133: protected void makeActions() {
134: URL url = getClass().getClassLoader().getResource(PRINT_ICON);
135: String str = I18N.get(I18N.MENU_FILE_PREFIX,
136: "SwingLE.action_print");
137: printAction = new AbstractAction(str, new ImageIcon(url, str)) {
138: public void actionPerformed(ActionEvent e) {
139: printReport();
140: }
141: };
142: printAction.putValue(Action.SHORT_DESCRIPTION, str);
143: printAction.setEnabled(false);
144:
145: str = I18N.get(I18N.MENU_FILE_PREFIX, "SwingLE.action_close");
146: closeAction = new AbstractAction(str) {
147: public void actionPerformed(ActionEvent e) {
148: close();
149: }
150: };
151:
152: url = getClass().getClassLoader().getResource(EXPORT_ICON);
153: str = I18N.get(I18N.MENU_FILE_PREFIX, "SwingLE.action_export");
154: exportAction = new AbstractAction(str, new ImageIcon(url, str)) {
155: public void actionPerformed(ActionEvent e) {
156: export();
157: }
158: };
159: exportAction.putValue(Action.SHORT_DESCRIPTION, str);
160:
161: url = getClass().getClassLoader().getResource(FIRST_ICON);
162: str = I18N.get(I18N.MENU_FILE_PREFIX,
163: "SwingLE.action_first_page");
164: goFirstAction = new AbstractAction(str, new ImageIcon(url, str)) {
165: public void actionPerformed(ActionEvent e) {
166: displayFirstPage();
167: }
168: };
169: goFirstAction.putValue(Action.SHORT_DESCRIPTION, str);
170: goFirstAction.setEnabled(false);
171:
172: url = getClass().getClassLoader().getResource(PREV_ICON);
173: str = I18N.get(I18N.MENU_FILE_PREFIX,
174: "SwingLE.action_previous_page");
175: goPrevAction = new AbstractAction(str, new ImageIcon(url, str)) {
176: public void actionPerformed(ActionEvent e) {
177: displayPrevPage();
178: }
179: };
180: goPrevAction.putValue(Action.SHORT_DESCRIPTION, str);
181: goPrevAction.setEnabled(false);
182:
183: url = getClass().getClassLoader().getResource(NEXT_ICON);
184: str = I18N.get(I18N.MENU_FILE_PREFIX,
185: "SwingLE.action_next_page");
186: goNextAction = new AbstractAction(str, new ImageIcon(url, str)) {
187: public void actionPerformed(ActionEvent e) {
188: displayNextPage();
189: }
190: };
191: goNextAction.putValue(Action.SHORT_DESCRIPTION, str);
192: goNextAction.setEnabled(false);
193:
194: url = getClass().getClassLoader().getResource(LAST_ICON);
195: str = I18N.get(I18N.MENU_FILE_PREFIX,
196: "SwingLE.action_last_page");
197: goLastAction = new AbstractAction(str, new ImageIcon(url, str)) {
198: public void actionPerformed(ActionEvent e) {
199: displayLastPage();
200: }
201: };
202: goLastAction.putValue(Action.SHORT_DESCRIPTION, str);
203: goLastAction.setEnabled(false);
204: }
205:
206: /**
207: * Creates the window menu.
208: *
209: * @param frame the window that will contain the menu
210: */
211: protected void makeMenu(JFrame frame) {
212: JMenuBar menuBar = new JMenuBar();
213: frame.setJMenuBar(menuBar);
214:
215: // File menu
216: JMenu menu = MenuUtils.readMenu("SwingLE.menu_file");
217: menuBar.add(menu);
218:
219: MenuUtils.addToMenu(menu, printAction, "SwingLE.action_print");
220: menu.addSeparator();
221: MenuUtils.addToMenu(menu, closeAction, "SwingLE.action_close");
222:
223: // View menu
224: menu = MenuUtils.readMenu("SwingLE.menu_view");
225: menuBar.add(menu);
226:
227: MenuUtils.addToMenu(menu, goFirstAction,
228: "SwingLE.action_first_page");
229: MenuUtils.addToMenu(menu, goPrevAction,
230: "SwingLE.action_previous_page");
231: MenuUtils.addToMenu(menu, goNextAction,
232: "SwingLE.action_next_page");
233: MenuUtils.addToMenu(menu, goLastAction,
234: "SwingLE.action_last_page");
235:
236: // Report menu
237: menu = MenuUtils.readMenu("SwingLE.menu_report");
238: menuBar.add(menu);
239:
240: MenuUtils
241: .addToMenu(menu, exportAction, "SwingLE.action_export");
242: }
243:
244: /**
245: * Creates and returns a new tool bar.
246: */
247: protected JToolBar makeToolbar() {
248: JToolBar bar = new JToolBar(
249: javax.swing.SwingConstants.HORIZONTAL);
250: bar.add(printAction);
251: bar.addSeparator();
252: bar.add(exportAction);
253: bar.addSeparator();
254: bar.add(goFirstAction);
255: bar.add(goPrevAction);
256: bar.add(goNextAction);
257: bar.add(goLastAction);
258: pageCountLabel = new JLabel(I18N
259: .get("SwingLE.loading_first_page"));
260: bar.addSeparator();
261: bar.add(pageCountLabel);
262: return bar;
263: }
264:
265: /**
266: * Creates a new page.
267: */
268: protected void doStartPage() {
269: int pageNum = pageContents.size() + 1;
270: pageBeingBuilt = new SwingPageContents(cardPanel, pageNum,
271: pageDim);
272: pageContents.add(pageBeingBuilt);
273: }
274:
275: /**
276: * At the end of the first page, starts building it in a separate thread.
277: * Check to see if the first page is done being built. If so, it is
278: * displayed.
279: */
280: protected void doEndPage() {
281: int numPages = pageContents.size();
282: if (numPages == 1) { // The first page
283: // Start building the page in a separate thread
284: pageBeingBuilt.prebuildPage();
285: pageCountLabel.setText(I18N
286: .get("SwingLE.building_first_page"));
287: }
288:
289: // If the first page is done being built and is not yet displayed,
290: // display it.
291: if (((SwingPageContents) pageContents.get(0)).isPageBuilt()
292: && displayPageNum == 0)
293: displayPage(1); // Updates page count label and nav actions
294: else {
295: updatePageCountLabel();
296: updateNavActions();
297: }
298: }
299:
300: /**
301: * Closes this window. Does not call <code>System.exit</code>.
302: */
303: public void close() {
304: if (frame != null) {
305: frame.setVisible(false);
306: frame.dispose();
307: }
308: wantsMoreData = false; // Signal report that we're done
309:
310: // Clean up memory a bit
311: pageContents = null;
312: pageBeingBuilt = null;
313: }
314:
315: /**
316: * Opens the report export dialog.
317: */
318: protected void export() {
319: new ExportWin(frame, report);
320: }
321:
322: /**
323: * Updates the navigation buttons based on number of pages and the current
324: * display page.
325: */
326: protected void updateNavActions() {
327: int numPages = pageContents.size();
328:
329: boolean canGoBack = numPages > 0 && displayPageNum > 1;
330: goFirstAction.setEnabled(canGoBack);
331: goPrevAction.setEnabled(canGoBack);
332:
333: boolean canGoForward = numPages > 0
334: && displayPageNum < numPages;
335: goNextAction.setEnabled(canGoForward);
336: goLastAction.setEnabled(canGoForward);
337: }
338:
339: /**
340: * Updates the page count label based on the current page number and the
341: * total number of pages.
342: */
343: protected void updatePageCountLabel() {
344: if (displayPageNum > 0)
345: pageCountLabel.setText(I18N.get("SwingLE.page") + ' '
346: + displayPageNum + ' ' + I18N.get("SwingLE.of")
347: + ' ' + pageContents.size());
348: }
349:
350: /** Performs the "First Page" command. */
351: protected void displayFirstPage() {
352: if (pageContents.size() > 0)
353: displayPage(1);
354: }
355:
356: /** Performs the "Next Page" command. */
357: protected void displayNextPage() {
358: if (pageContents.size() > displayPageNum)
359: displayPage(displayPageNum + 1);
360: }
361:
362: /** Performs the "Previous Page" command. */
363: protected void displayPrevPage() {
364: if (displayPageNum > 1)
365: displayPage(displayPageNum - 1);
366: }
367:
368: /** Performs the "Last Page" command. */
369: protected void displayLastPage() {
370: int numPages = pageContents.size();
371: if (numPages > 0)
372: displayPage(numPages);
373: }
374:
375: /**
376: * Fills the window with the contents of the specified page. If necessary,
377: * builds the page. Starts building the next and previous pages in separate
378: * threads, if they have not already been built.
379: *
380: * @param num page number, starting at 1
381: * @see SwingPageContents
382: */
383: protected void displayPage(int num) {
384: if (num == displayPageNum)
385: return;
386:
387: // Retrieve page. Build if necessary. Show it.
388: SwingPageContents contents = (SwingPageContents) pageContents
389: .get(num - 1);
390: if (!contents.isPageBuilt())
391: pageCountLabel.setText(I18N.get("SwingLE.building_page")
392: + ' ' + num + "...");
393:
394: int vertPosition = scroller.getVerticalScrollBar().getValue();
395: contents.showPage(); // Builds page if necessary
396: scroller.getVerticalScrollBar().setValue(vertPosition);
397:
398: // If next page not already built, start building it in a separate
399: // thread.
400: int numPages = pageContents.size();
401: if (numPages > num) { // Spawn thread to generate *next* page
402: contents = (SwingPageContents) pageContents.get(num);
403: contents.prebuildPage();
404: }
405:
406: // If previous page not already built, start building it in a separate
407: // thread.
408: // if (num > 2) { // We know pages 1 and 2 are already prebuilt
409: if (num > 1) {
410: contents = (SwingPageContents) pageContents.get(num - 2);
411: contents.prebuildPage();
412: }
413:
414: displayPageNum = num;
415: updatePageCountLabel();
416: updateNavActions();
417:
418: // Erase all other pages but the first and last pages and the two
419: // surrounding pages we just started generating; they are probably the
420: // most-visited.
421: for (int i = 1; i < (numPages - 1); ++i) {
422: if (i < (displayPageNum - 2) || i > (displayPageNum)) {
423: contents = (SwingPageContents) pageContents.get(i);
424: contents.forgetPage();
425: }
426: }
427: }
428:
429: /**
430: * Creates a new {@link SwingField} and adds it to the current page.
431: *
432: * @param field the report field
433: */
434: protected void doOutputField(Field field) {
435: if (!field.isVisible())
436: return;
437:
438: String fieldAsString = field.toString();
439: if (fieldAsString == null || fieldAsString.length() == 0) {
440: // FIX: implement makeBorders
441: // makeBorders(field);
442: return;
443: }
444:
445: jimm.datavision.field.Rectangle bounds = field.getBounds();
446:
447: // Page footers are anchored to page bottom.
448: int y = (currentSection.getArea().getArea() == SectionArea.PAGE_FOOTER) ? (int) (pageHeight()
449: - currentSection.getOutputHeight() + bounds.y)
450: : (int) (pageHeightUsed + bounds.y);
451: java.awt.Rectangle fieldBounds = new java.awt.Rectangle(
452: (int) bounds.x, y, (int) bounds.width, (int) field
453: .getOutputHeight());
454: pageBeingBuilt.add(field, fieldAsString, fieldBounds);
455:
456: // FIX: implement makeBorders
457: // makeBorders(field);
458: }
459:
460: protected void doOutputImage(ImageField image) {
461: doOutputField(image);
462: }
463:
464: /**
465: * Creates a new line. <em>Unimplemented</em>.
466: *
467: * @param line a line
468: */
469: protected void doOutputLine(Line line) {
470: }
471:
472: /**
473: * Prints the report.
474: */
475: public void printReport() {
476: final PrinterJob printJob = PrinterJob.getPrinterJob();
477: PageFormat format = report.getPaperFormat().getPageFormat();
478: SwingPrintBook book = new SwingPrintBook(pageContents, format);
479: printJob.setPageable(book);
480:
481: if (printJob.printDialog()) {
482: final StatusDialog statusDialog = new StatusDialog(frame,
483: I18N.get("SwingLE.print_report_title"), true, I18N
484: .get("SwingLE.print_report_status"));
485: book.setStatusDialog(statusDialog);
486:
487: new Thread(new Runnable() {
488: public void run() {
489: try {
490: printJob.print();
491: } catch (UserCancellationException uce) {
492: printJob.cancel();
493: } catch (Exception e) {
494: ErrorHandler.error(e);
495: } finally {
496: if (statusDialog != null)
497: statusDialog.dispose();
498: }
499: }
500: }).start();
501: }
502: }
503:
504: }
|