/*  J2FlowPrinterTestApplication
    (C) Copyright 2007
    Wildcrest Associates
    http://www.wildcrest.com
This source code may be freely used, modified, incorporated, and
distributed without restriction as part of any software that uses
J2PrinterWorks by Wildcrest Associates.
*/

import com.wildcrest.j2printerworks.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.math.*;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

public class J2FlowPrinterTestApplication {

    static J2Printer printer = new J2Printer(""); // make J2Printer instance
    // or to use new JDK 1.4 features: static J2Printer14 printer = new J2Printer14();
    // After purchase, use J2Printer("your-serial-number") or J2Printer14("your-serial-number")

    static JFrame frame;
    static JLabel status;

    static public void main(String args[]) {


// printer settings
        printer.setLeftMargin(.25); printer.setRightMargin(.25); printer.setTopMargin(.25); printer.setBottomMargin(.25);
        printer.setPrintPreviewScale(1.0);       // usually 0.5 but this makes things easier to see
//      printer.setParam("your serial number");  // include after you purchase J2PrinterWorks product


// Create application main window
        frame = new JFrame("J2FlowPrinterTestApplication");
        printer.setParentFrame(frame);
        printer.setBusyCursor(frame, true);

        frame.getContentPane().setLayout(new BorderLayout());
        frame.setSize(350,125);
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) { System.exit(0); }});

        JPanel buttonPanel = new JPanel();
        JButton printpreview = new JButton("Print Preview"); printpreview.setEnabled(false);
        JButton print = new JButton("Print"); print.setEnabled(false);
        JButton pagesetup = new JButton("Simple Page Setup"); pagesetup.setEnabled(false);
        JButton detailedpagesetup = new JButton("Detailed Page Setup"); detailedpagesetup.setEnabled(false);
        buttonPanel.setLayout(new GridLayout(2,2));
        buttonPanel.add(printpreview); buttonPanel.add(print);
        buttonPanel.add(pagesetup); buttonPanel.add(detailedpagesetup);
        frame.getContentPane().add(buttonPanel, "North");

        status = new JLabel();
        status.setHorizontalAlignment(JLabel.CENTER);
        status.setFont(new Font("SansSerif", Font.BOLD, 14));
        status.setForeground(Color.blue.darker().darker());
        status.setOpaque(true);
        frame.getContentPane().add(status, "South");

        print.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) { printer.print(); }});
        pagesetup.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) { printer.showPageSetupDialog(); windowToFront(); }});
        printpreview.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
              printer.showPrintPreviewDialog(frame);
              printer.setPrintPreviewPageNumber(printer.getCurrentPrintPreviewPageNumber());
              printer.setPrintPreviewScale(printer.getCurrentPrintPreviewScale());
              printer.setPrintPreviewTwoPageDisplay(printer.isCurrentPrintPreviewTwoPageDisplay());
            }});
        detailedpagesetup.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) { new J2FlowPrinterPageSetupDialog(frame, printer).setVisible(true); }});

        Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
        Rectangle frameDim = frame.getBounds();
        frame.setLocation((screenDim.width-frameDim.width)/2, (screenDim.height-frameDim.height)/2);
        frame.setVisible(true);
        status.setText("Begin document creation...");



// J2FlowPrinter test ------------------------------------------------------------------
// make J2FlowPrinter instance flowPrinter1
        status.setText("make flowPrinter 1 (multiple Flowables)");
        final J2FlowPrinter flowPrinter1 = new J2FlowPrinter();

        // first Flowable: a J2TextPane instance
        JTextPane paneA = J2TextPrinter.makeHTMLPane("<html><center>"
              + "<b><font face=Serif size=+1>J2FlowPrinter Test</font>"
              + "<br><font face=Serif size=+0>Wildcrest Associates</font></b>"
              + "<br><font face=Monospaced size=-1>http://www.wildcrest.com</font></tt>"
              + "<br><font face=SansSerif size=-1>January 29, 2007"
              + "<font face=SansSerif size=-3><br>&nbsp;</font>"
              + "<hr align=center width=50%>"
              + "<font face=SansSerif size=-3><br>&nbsp;</font>"

              + "<i>Abstract</i></center>"
              + "<font size=-2>This document demonstrates the use of the J2FlowPrinter "
              + "Java 2 printing component from Wildcrest Associates.  "
              + "It makes one Pageable from a series of "
              + "J2PrinterWorks printing components: "
              + "J2TextPrinter, J2TablePrinter, "
              + "J2TreePrinter, J2ListPrinter, J2PanelPrinter, J2ComponentPrinter, J2FlowPrinter itself, "
              + "VerticalGap, HorizontalLine, and PageEject, all printed back-to-back down the page.<br></font>"

              + "<br><font face=Serif size=+0><b>Flowables</b></font>"
              + "<br><font face=Serif size=-1>In addition to supporting "
              + "Pageables, J2PrinterWorks introduces a new printing "
              + "interface called Flowable.  Like Pageables, Flowables are printable components that may span "
              + "multiple pages, but unlike Pageables, they can also begin and end in the middle of any page.  "
              + "A J2FlowPrinter instance can group together any number of Flowable instances, "
              + "one immediately following after another back-to-back down the page.  "
              + "The resulting J2FlowPrinter instance is a Pageable (i.e., beginning and ending on page boundaries) "
              + "which represents a packed-together series of Flowables."
              + "<p>The following Flowables are provided by J2PrinterWorks:"
              + "<ul compact type=circle>"
              + "<li><b>J2TextPrinter</b> - a Flowable (as well as a Pageable) for JTextPane, JEditorPane, and JTextArea</li>"
              + "<li><b>J2TablePrinter</b> - a Flowable (as well as a Pageable) for JTable</li>"
              + "<li><b>J2TreePrinter</b> - a Flowable (as well as a Pageable) for JTree</li>"
              + "<li><b>J2ListPrinter</b> - a Flowable (as well as a Pageable) for JList</li>"
              + "<li><b>J2PanelPrinter</b> - a Flowable (as well as a Pageable) for JPanel</li>"
              + "<li><b>J2ComponentPrinter</b> - a Flowable (as well as a Pageable) for any Component</li>"
              + "<li><b>J2FlowPrinter</b> - is itself a Flowable as well as a Pageable (i.e., J2FlowPrinters are nestable)</li>"
              + "<li><b>HorizontalLine</b> - a Flowable for drawing a horizontal line between successive Flowables</li>"
              + "<li><b>VerticalGap</b> - a Flowable for leaving a gap between successive Flowables</li>"
              + "<li><b>PageEject</b> - a Flowable for starting the next Flowable at the top of a new page</li></ul>"
              + "In addition you can create and intermix your own custom "
              + "com.wildcrest.j2printerworks.Flowable instances.  "
              + "By stringing together a series of Flowables, "
              + "you can do things like include a JTable, a JTree, a JList, a Component, "
              + "a Graphics drawing, an Image, or the content pane of a program window "
              + "within an overall text document made up of JTextPane instances, "
              + "placing titles above and captions below any of these components.  "
              + "Each Flowable can be horizontally justified left, right, or center within the page, "
              + "and vertically justified top, bottom, or center within the remaining space in the page."

              + "<br><br>Up to this point, the first Flowable in our J2FlowPrinter instance "
              + "is a single J2TextPrinter instance specified using HTML.  Next, we will add a "
              + "PageEject instance as a second Flowable so our subsequent J2FlowPrinter Examples "
              + "section starts at the top of a new page.</font></html>"
        );
        status.setText("make textPrinter A");
        J2TextPrinter textPrinterA = new J2TextPrinter(paneA);
        if (System.getProperty("java.version").startsWith("1.2")) {
          textPrinterA.setDirectPrint(true);
        }


        // second Flowable is a PageEject instance

        // third Flowable: a J2TextPrinter instance
        JTextPane paneB = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=+0><b>J2FlowPrinter Examples</b></font>"
              + "<br><font face=Serif size=-1>After that PageEject, this is our third Flowable, another J2TextPrinter instance specified using HTML.  "
              + "In order to leave a little space between this text and the following J2TablePrinter instance, "
              + "our fourth flowable will be a VerticalGap instance, then we will "
              + "use a J2TablePrinter instance as our fifth Flowable.</font></html>"
        );
        status.setText("make textPrinter B");
        J2TextPrinter textPrinterB = new J2TextPrinter(paneB);
        if (System.getProperty("java.version").startsWith("1.2")) {
          textPrinterB.setDirectPrint(true);
        }

        // fourth Flowable is a VerticalGap instance

        // fifth Flowable: a J2TablePrinter instance
        String[] columnsA = {"Company Name", "Symbol", "99 Rank", "97 Rank", "Revenues"};
        Object[][] dataA = {
            {"General Motors", "GM", "1", "1", "$161,315"},
            {"Ford Motor", "F", "2", "2", "$144,416"},
            {"Wal-Mart", "WMT", "3", "4", "$139,208"},
            {"ExxonMobil", "XOM", "4", "3", "$100,697"},
            {"General Electric", "GE", "5", "5", "$100,469"},
            {"IBM", "IBM", "6", "6", "$81,667"},
            {"Citigroup", "C", "7", "40", "$76,431"},
            {"Philip Morris", "MO", "8", "10", "$57,813"},
            {"Boeing", "BA", "9", "36", "$56,154"},
            {"AT&T", "T", "10", "7", "$53,587"}};

        // test zero-size: columnsA = new String[0];
        // test zero-size: Object [][] dataA = { };

        JTable tableA = new JTable(dataA, columnsA);
        tableA.getColumnModel().getColumn(0).setPreferredWidth(125);
        tableA.getColumnModel().getColumn(1).setPreferredWidth(75);
        tableA.getColumnModel().getColumn(2).setPreferredWidth(65);
        tableA.getColumnModel().getColumn(3).setPreferredWidth(65);
        tableA.getColumnModel().getColumn(4).setPreferredWidth(100);
        tableA.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        status.setText("make tablePrinter A");
        J2TablePrinter tablePrinterA = new J2TablePrinter(tableA);
        tablePrinterA.setColumnHeaderPrinting(J2TablePrinter.NONE);

        // sixth Flowable: a J2PanelPrinter instance
        J2Label labelA = new J2Label("<html>"
              + "<p><font face=SansSerif size=-2>This caption is the sixth Flowable, a J2PanelPrinter "
              + "instance used to print<br>a J2Label <i>defined using HTML</i>.  "
              + "Such a caption "
              + "could also be done using a<br><b>JTextPane in a J2PanelPrinter instance</b> "
              + "or using a <b>J2TextPrinter instance</b>.</font></html>"
        );

        // alternatively, instead of forcing line breaks with <br>,
        // you can specify preferred width and get J2Label to wrap itself,
        // but due to Java bug this has to be displayed on screen for this to work
        //JLabel labelA = new J2Label("<html>"
        //      + "<p><font face=SansSerif size=-2>This is the sixth Flowable, a J2PanelPrinter "
        //      + "instance used to print a J2Label instance <i>defined using HTML</i>.  "
        //      + "Such a caption "
        //      + "could also be done using a<br><b>JTextPane in a J2PanelPrinter instance</b> "
        //      + "or using a <b>J2TextPrinter instance</b>.</font></html>"
        //);
        //labelA.setPreferredSize(new Dimension(400,50));   // use this to control width, pack will cause layout

        status.setText("make panelPrinter A");
        J2PanelPrinter panelPrinterA = new J2PanelPrinter(labelA);

        // seventh Flowable is a VerticalGap instance

        // eighth Flowable: a J2TextPrinter instance
        JTextPane paneC = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>Right before this text was our seventh Flowable, another VerticalGap.  "
              + "This text is out eighth Flowable, another J2TextPrinter instance defined using HTML.  "
              + "We will now show how a drawing might be inserted in our document.  "
              + "The figure is drawn using the paint method of a JPanel using "
              + "standard Java Graphics or Graphics2D drawing methods.  "
              + "The resulting JPanel is printed using a J2PanelPrinter, giving us our ninth Flowable.</font></html>");
        status.setText("make textPrinter C");
        J2TextPrinter textPrinterC = new J2TextPrinter(paneC);
        if (System.getProperty("java.version").startsWith("1.2")) {
          textPrinterC.setDirectPrint(true);
        }


        // ninth Flowable: a J2PanelPrinter instance
        JPanel drawing = new JPanel() {
            public Dimension getPreferredSize() { return new Dimension(180,150); }
            public void paint(Graphics g) {
                g.setColor(Color.white); Rectangle r = getBounds(); g.fillRect(r.x, r.y, r.width, r.height);
                g.setColor(Color.black); g.drawLine(25,135,25,25); g.drawLine(15,125,175,125);
                g.setColor(Color.yellow); g.fillRect(50,100,25,25);
                g.setColor(Color.black); g.drawRect(50,100,25,25);
                g.setColor(Color.pink); g.fillRect(85,75,25,50);
                g.setColor(Color.black); g.drawRect(85,75,25,50);
                g.setColor(Color.orange); g.fillRect(120,50,25,75);
                g.setColor(Color.black); g.drawRect(120,50,25,75);
                g.setFont(new Font("SansSerif",Font.PLAIN,10));
                g.drawString("Graphics",40,95);
                g.drawString("in a",90,70);
                g.drawString("JPanel",115,45);
            }
        };
        status.setText("make panelPrinter B");
        J2PanelPrinter panelPrinterB = new J2PanelPrinter(drawing);

        // tenth Flowable: a J2TextPrinter instance
        JTextPane paneD = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>This text is out tenth Flowable, another J2TextPrinter instance defined using HTML.  "
              + "We will next show how an Image might be inserted in our document.  "
              + "The Image will be drawn using the Image constructor of J2PanelPrinter."
              + "This will be our eleventh Flowable.  If the Image is big, it will jump "
              + "to the top of the next page if it doesn't fit here.  This J2PanelPrinter is in \"shrink-to-fit\""
              + "mode and will chose between the remainder of this page or the next full page based "
              + "on which requires the least scaling.  Alternatively, you could print this J2PanelPrinter "
              + "instance using \"tile\""
              + "mode which would keep the Image unscaled and break it as necessary across multiple pages horizontally "
              + "and vertically.</font></html>");
        status.setText("make textPrinter D");
        J2TextPrinter textPrinterD = new J2TextPrinter(paneD);
        if (System.getProperty("java.version").startsWith("1.2")) {
          textPrinterD.setDirectPrint(true);
        }

        // eleventh Flowable: a J2PanelPrinter instance
        status.setText("make panelPrinter C");
        J2PanelPrinter panelPrinterC = new J2PanelPrinter(new ImageIcon("images"+File.separator+"CraterLake.jpg").getImage());
        //default is SHRINK_TO_FIT, or can test: panelPrinterC.setPageRule(J2PanelPrinter.TILE);
        //with TILE can also test: panelPrinterC.setMaximumPaginationGap(0.8);

        // twelfth Flowable: a J2TextPrinter instance
        JTextPane paneE = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>This text is out twelfth Flowable, another J2TextPrinter instance defined using HTML.  "
              + "We will now create a small JTree file directory and print it as a Flowable using a J2TreePrinter instance "
              + "which will be our thirteenth Flowable.</font></html>");
        status.setText("make textPrinter E");
        J2TextPrinter textPrinterE = new J2TextPrinter(paneE);
        if (System.getProperty("java.version").startsWith("1.2")) {
         textPrinterE.setDirectPrint(true);
       }

        // thirteenth Flowable: a J2TreePrinter instance
        JTree treeA = makeFileTree("."+File.separator+"images");
        expandAllRows(treeA, 75);
        status.setText("make treePrinter A");
        final J2TreePrinter treePrinterA = new J2TreePrinter(treeA);

        // fourteenth Flowable: a J2TextPrinter instance
        JTextPane paneF = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>This text is our fourteenth Flowable, another J2TextPrinter instance defined using HTML.  "
              + "We will now show a series of HorizontalLine instances, which may be used to separate "
              + "sections of a document.  We can specify horizontal line heights, the gap above and below "
              + "the horizontal line, the color of the horizontal line, and the width of the horizontal line, "
              + "either as a absolute width or a distance in from the page margins."
              + "The following are some examples:</font></html>");
        status.setText("make textPrinter F");
        J2TextPrinter textPrinterF = new J2TextPrinter(paneF);
        if (System.getProperty("java.version").startsWith("1.2")) {
          textPrinterF.setDirectPrint(true);
        }


        // fifteenth Flowable: a nested J2FlowPrinter instance with alternating HorizontalLines and J2PanelPrinters for J2Labels
        J2FlowPrinter flowPrinter2 = new J2FlowPrinter();
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This is the default HorizontalLine, which is 4 pixels high and "
            + "inset 1 inch from<br>left & right margins, has 16 pixels above and below, and is light gray:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine());
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 1 pixel high "
            + "and 100 pixels wide,<br>has 0 pixels above and below, and is blue:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(0,1,100,Color.blue));
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 10 pixels high "
            + "and 375 pixels wide,<br>has 5 pixels above and below, and is green:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(5,10,375,Color.green));
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 5 pixels high "
            + "and 5 pixels wide,<br>has 10 pixels above and below, and is red:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(10,5,5,Color.red));
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 20 pixels high "
            + "and 200 pixels wide,<br>has 5 pixels above and below, and is orange:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(5,20,200,Color.orange));
        flowPrinter2.addFlowable(new J2PanelPrinter(new J2Label("<html><p><font size=-2>This HorizontalLine is 50 pixels high "
            + "and 1 pixel wide,<br>has 0 pixels above and below, and is dark gray:</font></html>")));
        flowPrinter2.addFlowable(new HorizontalLine(0,50,1,Color.darkGray));

        // sixteenth Flowable: a J2TextPrinter instance
        JTextPane paneG = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>This J2TextPrinter is our sixteenth Flowable.  "
              + "However, the HorizontalLine examples above were done with "
              + "6 HorizontalLine instances alternating with 6 J2PanelPrinter instances (printing "
              + "J2Labels).  All 12 of these Flowables were placed inside their own "
              + "J2FlowPrinter instance.  Since J2FlowPrinter is itself a Flowable, this J2FlowPrinter was "
              + "simply added to our overall J2FlowPrinter, showing that J2FlowPrinter instances "
              + "can be nested.  "
              + "The only restriction is that no Flowable instance can "
              + "appear twice in the same overall J2FlowPrinter instance.<br><br>"
              + "It is also possible to scale Flowables individually within a J2FlowPrinter.  "
              + "This can be particularly useful in getting tables or images to fit nicely as figures "
              + "within an overall text document.  For examples, here is an image we've seen earlier.  The "
              + "actual image is much wider than one page.  The previous time we saw this image scaled to "
              + "exactly one landscape page wide and printed on its own page.  This time we will scale it "
              + "down to half the width of a portrait page, as we might do for a image or graphic representing "
              + "a figure in a paper."
              + "</font></html>");
        status.setText("make textPrinter G");
        J2TextPrinter textPrinterG = new J2TextPrinter(paneG);
        if (System.getProperty("java.version").startsWith("1.2")) {
          textPrinterG.setDirectPrint(true);
        }

        // seventeenth Flowable: a J2PanelPrinter instance with an image
        Image imgH = new ImageIcon("images" + File.separator + "CraterLake.jpg").getImage();
        status.setText("make panelPrinter H");
        J2PanelPrinter panelPrinterH = new J2PanelPrinter(imgH);
        panelPrinterH.setScale(printer.getBodyWidth() / imgH.getWidth(frame) / 2.0);

        // eighteenth Flowable: a J2TextPrinter instance
        JTextPane paneI = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1>Or, here is a JTable we've seen before.  Previously, this was "
              + "printed as a Pageable on its own landscape page and scaled to fit within exactly one page high and one page wide.  "
              + "This time we will print it on a portrait page and scale it to exactly one page wide with no "
              + "scaling to control the length so that the whole table can span vertically across more than "
              + "one page as necessary.  This is appropriate since this time we are including "
              + "it as a Flowable so that it prints "
              + "back-to-back with our other Flowable instances."
              + "</font></html>");
        status.setText("make textPrinter I");
        J2TextPrinter textPrinterI = new J2TextPrinter(paneI);
        if (System.getProperty("java.version").startsWith("1.2")) {
          textPrinterI.setDirectPrint(true);
        }

        // nineteenth Flowable: a J2TablePrinter instance
        // make J2TablePrinter instance
                Vector colsTable2 = new Vector();
                colsTable2.addElement("Rank"); colsTable2.addElement("Symbol");
                colsTable2.addElement("Company"); colsTable2.addElement("Price");
                colsTable2.addElement("Shares"); colsTable2.addElement("Market Cap");
                colsTable2.addElement("Revenue"); colsTable2.addElement("F500 rank");
                colsTable2.addElement("MarketCap/Rev"); colsTable2.addElement("MarketCap/Rev rank");
                colsTable2.addElement("Employees"); colsTable2.addElement("Rev/Emp");
                colsTable2.addElement("Rev/Emp rank");

                Vector dataTable2 = new Vector();
                try {
                    BufferedReader in = new BufferedReader(new FileReader("F100.txt"));
                    in.readLine();        // throw away first line (headers)
                    for (int k=0; k<50; k++) {
                        String line = in.readLine();
                        StringTokenizer st = new StringTokenizer(line,"\t");
                        Vector vec = new Vector();
                        for (int i=0; i<13; i++) vec.addElement(st.nextToken());
                        dataTable2.addElement(vec);
                    }
                    in.close();
                } catch (Exception e) { }

                JTable table2 = new JTable(dataTable2, colsTable2);
                table2.getColumnModel().getColumn(0).setPreferredWidth(45);
                table2.getColumnModel().getColumn(1).setPreferredWidth(55);
                table2.getColumnModel().getColumn(2).setPreferredWidth(205);
                table2.getColumnModel().getColumn(3).setPreferredWidth(45);
                table2.getColumnModel().getColumn(4).setPreferredWidth(55);
                table2.getColumnModel().getColumn(5).setPreferredWidth(80);
                table2.getColumnModel().getColumn(6).setPreferredWidth(70);
                table2.getColumnModel().getColumn(7).setPreferredWidth(75);
                table2.getColumnModel().getColumn(8).setPreferredWidth(105);
                table2.getColumnModel().getColumn(9).setPreferredWidth(130);
                table2.getColumnModel().getColumn(10).setPreferredWidth(80);
                table2.getColumnModel().getColumn(11).setPreferredWidth(70);
                table2.getColumnModel().getColumn(12).setPreferredWidth(90);
                table2.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

        // make J2TablePrinter instance tablePrinterJ
               status.setText("make tablePrinter J");
               J2TablePrinter tablePrinterJ = new J2TablePrinter(table2);
               tablePrinterJ.setMaximumPages(1, 0);

        // twentieth Flowable: a J2TextPrinter instance
        JTextPane paneK = J2TextPrinter.makeHTMLPane("<html>"
              + "<font face=Serif size=-1> This J2TextPrinter is now Flowable number twenty.  "
              + "We could now append any other Flowables and they would continue to print "
              + "back-to-back down the page.  In this manner, documents and reports made up of "
              + "many different kinds of content can be formed."
              + "</font></html>");
        status.setText("make textPrinter K");
        J2TextPrinter textPrinterK = new J2TextPrinter(paneK);
        if (System.getProperty("java.version").startsWith("1.2")) {
          textPrinterK.setDirectPrint(true);
        }

// Now string together all the Flowables
// Note same Flowable instance cannot appear twice in same J2FlowPrinter instance!
        status.setText("add Flowables");
        flowPrinter1.addFlowable(textPrinterA);
        //flowPrinter1.addFlowable(new PageEject());
        flowPrinter1.addFlowable(textPrinterB);
        //test: flowPrinter1.addFlowable(new VerticalGap(0.0));
        flowPrinter1.addFlowable(new VerticalGap());
        flowPrinter1.addFlowable(tablePrinterA);
        flowPrinter1.addFlowable(panelPrinterA);
        flowPrinter1.addFlowable(new VerticalGap());
        flowPrinter1.addFlowable(textPrinterC);

        /* test:
        String taText = "This is a test of the J2TextPrinter String constructor, which puts the string "
                                                   + "into a JTextPane and optionally lets you specify the Font.  "
                                                   + "Text will be wrapped to the width of the page, left justified, and black.  "
                                                   + "If no font is specified, the default is SansSerif, PLAIN, 12.";
        flowPrinter1.addFlowable(new J2TextPrinter(taText, new Font("Serif",Font.PLAIN,12)));
        flowPrinter1.addFlowable(new J2TextPrinter(taText));
        */

        /* test centering 1:
        JTextPane paneKKK = J2TextPrinter.makeHTMLPane("<html>"
             + "<font face=SansSerif size=-1><center><b>Here is a string</b></center></font></html>");
        flowPrinter1.addFlowable(new J2TextPrinter(paneKKK));
        */

        /* test centering 2:
        JTextPane paneKK = new JTextPane();
        SimpleAttributeSet aset = J2TextPrinter.makeSimpleAttributeSet(
           "SansSerif", "Bold", 12, StyleConstants.ALIGN_CENTER, Color.black);
        J2TextPrinter.appendStyledText(paneKK, "Here is a string", aset);
        flowPrinter1.addFlowable(new J2TextPrinter(paneKK));
        */

       /* test centering 3:
       J2TextPrinter j2txtp = new J2TextPrinter("Here is a string", new Font("SansSerif", Font.BOLD, 12));
       SimpleAttributeSet attribute = new SimpleAttributeSet();
       StyleConstants.setAlignment(attribute, StyleConstants.ALIGN_CENTER);
       j2txtp.getPane().setParagraphAttributes(attribute,true);
       flowPrinter1.addFlowable(j2txtp);
       */

        flowPrinter1.addFlowable(panelPrinterB);
        flowPrinter1.addFlowable(textPrinterD);
        flowPrinter1.addFlowable(panelPrinterC);
        flowPrinter1.addFlowable(textPrinterE);
        flowPrinter1.addFlowable(treePrinterA);
        flowPrinter1.addFlowable(textPrinterF);
        flowPrinter1.addFlowable(flowPrinter2);
        flowPrinter1.addFlowable(textPrinterG);
        flowPrinter1.addFlowable(panelPrinterH);
        flowPrinter1.addFlowable(textPrinterI);
        flowPrinter1.addFlowable(tablePrinterJ);
        flowPrinter1.addFlowable(textPrinterK);

        String path = new File("images").getAbsolutePath();

        flowPrinter1.setLeftFooter(J2Printer.FIRST, new J2Label("<html><img src=\"file:///"+path+"/J2PrinterWorksC32.gif\"></html>"));
        flowPrinter1.setCenterFooter(J2Printer.FIRST,new J2Label("<html><div align=center><font size=+1>J2FlowPrinterTestApplication</html>"));
        flowPrinter1.setRightFooter(J2Printer.FIRST,new J2Label("<html><div align=right><font size=-1>|||EEE, MMM d, yyyy|||<br>|||hh:mm:ss a zzz|||</html>"));
        flowPrinter1.setFooterStyle(J2Printer.FIRST, J2Printer.NONE);

// Finally, add all the Pageables to create the overall printer document
        status.setText("add Pageables");
        printer.addPageable(flowPrinter1);

        // test N-up printing: printer.setNup(9);
        // test monochrome print preview dialog: printer.setPageImagesMonochrome(true);

        printpreview.setEnabled(true);
        print.setEnabled(true);
        pagesetup.setEnabled(true);
        detailedpagesetup.setEnabled(true);
        frame.toFront();
        printer.setBusyCursor(frame, false);
        status.setText("Ready");

   }

// make JTree for file directories
	static JTree makeFileTree(String path) {
	    DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("root");
	    File f = new File(path);
	    if (path.equals(".")) f = new File(new File(new File(new File(path).list()[0]).getAbsolutePath()).getParent());
	    buildTree(rootNode,f.getParent(),f.getName()); // recursive
	    return new JTree(rootNode.getFirstChild());
	}

	static void buildTree(DefaultMutableTreeNode node, String path, String file) {
	    path = path+File.separator+file;
	    File f = new File(path);
	    if (f.isDirectory()) {
	        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(file);
	        String[] files = f.list();
	        for (int i=0; i<files.length; i++) buildTree(newNode, path, files[i]);
	        node.add(newNode);
	    }
	    else node.add(new DefaultMutableTreeNode(file, false));
	}

	static void expandAllRows(JTree tree, int enough) {     // brute force JTree row expander
	    while (true) {
	        int rowCount = tree.getRowCount();
                if (rowCount>=enough) break;
	        for (int i=0; i<rowCount; i++) tree.expandRow(i);
                if (rowCount==tree.getRowCount()) break;
	    }
	}


// make random JTables (not used above but available for testing)
    static Random rand = new Random(12345);
    static int tableNo=0;
    static JTable randomTable(int nRows, int nCols) {
        tableNo++;
        String[] columns = new String[nCols];
        for (int i=0; i<nCols; i++) columns[i] = new String("Col"+(i+1));
        Object[][] data = new Object[nRows][nCols];
        for (int i=0; i<nRows; i++) {
            data[i][0] = new String("Row"+(i+1));
            for (int j=1; j<nCols; j++)
                data[i][j] = new String("Table"+tableNo+ " " + rand.nextInt());
        }
        data[0][0] = new String(nRows+" X "+nCols);
        JTable jt = new JTable(data,columns);
        JFrame f = new JFrame();
        f.getContentPane().add(new JScrollPane(jt), "Center");   // construct table
        // to randomize: int width = 100+(int)(rand.nextDouble()*nCols*150);
        int width = 100+nCols*75;
        jt.setPreferredSize(new Dimension(width,16*nRows));
        jt.setBounds(0,0,width,16*nRows);

        f.pack();   // required
        f.setBounds(0,0,width,16*nRows);
        //f.setVisible(true);
        return jt;
    }



    static class MyPrintingEventHandler extends PrintingEventHandler {
      int totalPages;

      public void printingStart() {
        showProgress("About to print..."); // printing started
        totalPages = printer.getPageable().getNumberOfPages(); // expensive so do only once
      }

      public void printingDone() {
        windowToFront(); // needed prior to JDK 1.4.1 due to Bug Parade #4514422
        brieflyShowProgress("Printing done");
      }

      public void printDialogOK() {
        windowToFront(); // needed prior to JDK 1.4.1 due to Bug Parade #4514422
        showProgress("Start printing...");
      }

      public void printDialogCanceled() {
        windowToFront(); // needed prior to JDK 1.4.1 due to Bug Parade #4514422
        brieflyShowProgress("Printing canceled");
      }

      public void pageStart(int pageNum) {
        showProgress("Printing page " + pageNum + " of " + totalPages);
      }

      public void pageSetupDialogOK() {
         brieflyShowProgress("Page setup dialog OK");
      }

      public void pageSetupDialogCanceled() {
         brieflyShowProgress("Page setup dialog canceled");
      }

      public void exceptionThrown(Exception e) {
        showProgress("Got an exception: " + e);
      }

    }

    static private void showProgress(String str) { status.setText(str); }

    static private void brieflyShowProgress(String str) {
      showProgress(str);
      new Thread() { public void run() {
          try { sleep(1500); } catch (Exception e) {}
          SwingUtilities.invokeLater( new Runnable() { public void run() { showProgress(" "); } } );
      } }.start(); // from different thread, wait 1 sec then erase
    }

    static void windowToFront() { frame.toFront(); } // needed under JDK 1.4 due to Bug Parade #4514422

}

