001: package net.sourceforge.squirrel_sql.fw.datasetviewer;
002:
003: /*
004: * Copyright (C) 2001-2002 Colin Bell
005: * colbell@users.sourceforge.net
006: * Modifications copyright (C) 2001-2002 Johan Compagner
007: * jcompagner@j-com.nl
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: */
023: import java.awt.Component;
024: import java.awt.Point;
025: import java.awt.event.MouseAdapter;
026: import java.awt.event.MouseEvent;
027: import java.awt.event.KeyEvent;
028: import java.awt.event.MouseMotionAdapter;
029:
030: import javax.swing.DefaultCellEditor;
031: import javax.swing.JTable;
032: import javax.swing.ListSelectionModel;
033: import javax.swing.table.DefaultTableColumnModel;
034: import javax.swing.table.TableCellEditor;
035: import javax.swing.table.TableColumnModel;
036: import javax.swing.JOptionPane;
037:
038: //??import javax.swing.event.TableModelListener;
039: //??import javax.swing.event.TableModelEvent;
040:
041: import net.sourceforge.squirrel_sql.fw.gui.ButtonTableHeader;
042: import net.sourceforge.squirrel_sql.fw.gui.SortableTableModel;
043: import net.sourceforge.squirrel_sql.fw.gui.TablePopupMenu;
044: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
045: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
046: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
047: import net.sourceforge.squirrel_sql.fw.util.StringManager;
048: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.CellComponentFactory;
049: import net.sourceforge.squirrel_sql.fw.datasetviewer.cellcomponent.RestorableJTextField;
050:
051: // for printing...
052:
053: import java.awt.print.Printable;
054: import java.awt.Graphics;
055: import java.awt.print.PageFormat;
056: import java.awt.print.PrinterException;
057: import java.awt.Graphics2D;
058: import java.awt.Color;
059: import javax.swing.table.JTableHeader;
060: import java.awt.Font;
061:
062: public class DataSetViewerTablePanel extends
063: BaseDataSetViewerDestination implements IDataSetTableControls,
064: Printable {
065:
066: private static final StringManager s_stringMgr = StringManagerFactory
067: .getStringManager(DataSetViewerTablePanel.class);
068:
069: private ILogger s_log = LoggerController
070: .createLogger(DataSetViewerTablePanel.class);
071:
072: private MyJTable _table = null;
073: private MyTableModel _typedModel;
074: private IDataSetUpdateableModel _updateableModel;
075:
076: public DataSetViewerTablePanel() {
077: super ();
078: }
079:
080: public void init(IDataSetUpdateableModel updateableModel) {
081: _table = new MyJTable(this , updateableModel);
082: _updateableModel = updateableModel;
083: }
084:
085: public IDataSetUpdateableModel getUpdateableModel() {
086: return _updateableModel;
087: }
088:
089: public void clear() {
090: _typedModel.clear();
091: _typedModel.fireTableDataChanged();
092: }
093:
094: public void setColumnDefinitions(ColumnDisplayDefinition[] colDefs) {
095: super .setColumnDefinitions(colDefs);
096: _table.setColumnDefinitions(colDefs);
097: }
098:
099: public void moveToTop() {
100: if (_table.getRowCount() > 0) {
101: _table.setRowSelectionInterval(0, 0);
102: }
103: }
104:
105: /**
106: * Get the component for this viewer.
107: *
108: * @return The component for this viewer.
109: */
110: public Component getComponent() {
111: return _table;
112: }
113:
114: /*
115: * @see BaseDataSetViewerDestination#addRow(Object[])
116: */
117: protected void addRow(Object[] row) {
118: _typedModel.addRow(row);
119: }
120:
121: /*
122: * @see BaseDataSetViewerDestination#getRow(row)
123: */
124: protected Object[] getRow(int row) {
125: Object values[] = new Object[_typedModel.getColumnCount()];
126: for (int i = 0; i < values.length; i++)
127: values[i] = _typedModel.getValueAt(row, i);
128: return values;
129: }
130:
131: /*
132: * @see BaseDataSetViewerDestination#allRowsAdded()
133: */
134: protected void allRowsAdded() {
135: _typedModel.fireTableStructureChanged();
136: _table.initColWidths();
137: }
138:
139: /*
140: * @see IDataSetViewer#getRowCount()
141: */
142: public int getRowCount() {
143: return _typedModel.getRowCount();
144: }
145:
146: public void setShowRowNumbers(boolean showRowNumbers) {
147: _table.setShowRowNumbers(showRowNumbers);
148: }
149:
150: /*
151: * The JTable used for displaying all DB ResultSet info.
152: */
153: protected final class MyJTable extends JTable {
154: private final int _multiplier;
155: private static final String data = "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG";
156:
157: private TablePopupMenu _tablePopupMenu;
158: private IDataSetTableControls _creator;
159: private Point _dragBeginPoint = null;
160: private Point _dragEndPoint = null;
161: private RowNumberTableColumn _rntc;
162: private ButtonTableHeader _tableHeader = new ButtonTableHeader();
163:
164: MyJTable(IDataSetTableControls creator,
165: IDataSetUpdateableModel updateableObject) {
166: super (new SortableTableModel(new MyTableModel(creator)));
167: _creator = creator;
168: _typedModel = (MyTableModel) ((SortableTableModel) getModel())
169: .getActualModel();
170: _multiplier = getFontMetrics(getFont()).stringWidth(data)
171: / data.length();
172: setRowHeight(getFontMetrics(getFont()).getHeight());
173: boolean allowUpdate = false;
174: // we want to allow editing of read-only tables on-demand, but
175: // it would be confusing to include the "Make Editable" option
176: // when we are already in edit mode, so only allow that option when
177: // the background model is updateable AND we are not already editing
178: if (updateableObject != null && !creator.isTableEditable())
179: allowUpdate = true;
180: createGUI(allowUpdate, updateableObject);
181:
182: // just in case table is editable, call creator to set up cell editors
183: _creator.setCellEditors(this );
184:
185: /*
186: * TODO: When 1.4 is the earliest version supported, add the following line:
187: * setSurrendersFocusOnKeystroke(true);
188: * This should help handle some problems with navigation using tab & return
189: * to move through cells.
190: */
191:
192: addMouseListener(new MouseAdapter() {
193: public void mousePressed(MouseEvent e) {
194: _dragBeginPoint = e.getPoint();
195: }
196:
197: public void mouseReleased(MouseEvent e) {
198: _dragBeginPoint = null;
199: _dragEndPoint = null;
200: repaint();
201: }
202: });
203:
204: addMouseMotionListener(new MouseMotionAdapter() {
205: public void mouseDragged(MouseEvent e) {
206: onMouseDragged(e);
207: repaint();
208: }
209: });
210:
211: }
212:
213: private void onMouseDragged(MouseEvent e) {
214: if (null == _dragBeginPoint) {
215: _dragBeginPoint = e.getPoint();
216: }
217:
218: _dragEndPoint = e.getPoint();
219: }
220:
221: public void paint(Graphics g) {
222: super .paint(g);
223:
224: if (null != _dragBeginPoint && null != _dragEndPoint
225: && false == _dragBeginPoint.equals(_dragEndPoint)) {
226: int x = Math.min(_dragBeginPoint.x, _dragEndPoint.x);
227: int y = Math.min(_dragBeginPoint.y, _dragEndPoint.y);
228: int width = Math.abs(_dragBeginPoint.x
229: - _dragEndPoint.x);
230: int heigh = Math.abs(_dragBeginPoint.y
231: - _dragEndPoint.y);
232:
233: Color colBuf = g.getColor();
234: g.setColor(getForeground());
235: g.drawRect(x, y, width, heigh);
236: g.setColor(colBuf);
237: }
238: }
239:
240: public IDataSetTableControls getCreator() {
241: return _creator;
242: }
243:
244: /*
245: * override the JTable method so that whenever something asks for
246: * the cellEditor, we save a reference to that cell editor.
247: * Our ASSUMPTION is that the cell editor is only requested
248: * when it is about to be activated.
249: */
250: public TableCellEditor getCellEditor(int row, int col) {
251: TableCellEditor cellEditor = super .getCellEditor(row, col);
252: currentCellEditor = (DefaultCellEditor) cellEditor;
253: return cellEditor;
254: }
255:
256: /**
257: * There are two special cases where we need to override the default behavior
258: * when we begin cell editing. For some reason, when you use the keyboard to
259: * enter a cell (tab, enter, arrow keys, etc), the first character that you type
260: * after entering the field is NOT passed through the KeyListener mechanism
261: * where we have the special handling in the DataTypes. Instead, it is passed
262: * through the KeyMap and Action mechanism, and the default Action on the
263: * JTextField is to add the character to the end of the existing text, or if it is delete
264: * to delete the last character of the existing text. In most cases, this is ok, but
265: * there are three special cases of which we only handle two here:
266: * - If the data field currently contains "<null>" and the user types a character,
267: * we want that character to replace the string "<null>", which represents the
268: * null value. In this case we process the event normally, which usually adds
269: * the char to the end of the string, then remove the char afterwards.
270: * We take this approach rather than just immediately replacing the "<null>"
271: * with the char because there are some chars that should not be put into
272: * the editable text, such as control-characters.
273: * - If the data field contains "<null>" and the user types a delete, we do not
274: * want to delete the last character from the string "<null>" since that string
275: * represents the null value. In this case we simply ignore the user input.
276: * - Whether or not the field initially contains null, we do not run the input validation
277: * function for the DataType on the input character. This means that the user
278: * can type an illegal character into the field. For example, after entering an
279: * Integer field by typing a tab, the user can enter a letter (e.g. "a") into that
280: * field. The normal keyListener processing prevents that, but we cannot
281: * call it from this point. (More accurately, I cannot figure out how to do it
282: * easilly.) Thus the user may enter one character of invalid data into the field.
283: * This is not too serious a problem, however, because the normal validation
284: * is still done when the user leaves the field and it SQuirreL tries to convert
285: * the text into an object of the correct type, so errors of this nature will still
286: * be caught. They just won't be prevented.
287: */
288: public void processKeyEvent(KeyEvent e) {
289:
290: // handle special case of delete with <null> contents
291: if (e.getKeyChar() == '\b'
292: && getEditorComponent() != null
293: && ((RestorableJTextField) getEditorComponent())
294: .getText().equals("<null>")) {
295: //ignore the user input
296: return;
297: }
298:
299: // generally for KEY_TYPED this means add the typed char to the end of the text,
300: // but there are some things (e.g. control chars) that are ignored, so let the
301: // normal processing do its thing
302: super .processKeyEvent(e);
303:
304: // now check to see if the original contents were <null>
305: // and we have actually added the input char to the end of it
306: if (getEditorComponent() != null) {
307: if (e.getID() == KeyEvent.KEY_TYPED
308: && ((RestorableJTextField) getEditorComponent())
309: .getText().length() == 7) {
310: // check that we did not just add a char to a <null>
311: if (((RestorableJTextField) getEditorComponent())
312: .getText()
313: .equals("<null>" + e.getKeyChar())) {
314: // replace the null with just the char
315: ((RestorableJTextField) getEditorComponent())
316: .updateText("" + e.getKeyChar());
317: }
318: }
319: }
320:
321: }
322:
323: /*
324: * When user leaves a cell after editing it, the contents of
325: * that cell need to be converted from a string into an
326: * object of the appropriate type before updating the table.
327: * However, when the call comes from the Popup window, the data
328: * has already been converted and validated.
329: * We assume that a String being passed in here is a value from
330: * a text field that needs to be converted to an object, and
331: * a non-string object has already been validated and converted.
332: */
333: public void setValueAt(Object newValueString, int row, int col) {
334: if (!(newValueString instanceof java.lang.String)) {
335: // data is an object - assume already validated
336: super .setValueAt(newValueString, row, col);
337: return;
338: }
339:
340: // data is a String, so we need to convert to real object
341: StringBuffer messageBuffer = new StringBuffer();
342:
343: int modelIndex = getColumnModel().getColumn(col)
344: .getModelIndex();
345: ColumnDisplayDefinition colDef = getColumnDefinitions()[modelIndex];
346: Object newValueObject = CellComponentFactory
347: .validateAndConvert(colDef, getValueAt(row, col),
348: (String) newValueString, messageBuffer);
349:
350: if (messageBuffer.length() > 0) {
351:
352: // i18n[dataSetViewerTablePanel.textCantBeConverted=The given text cannot be converted into the internal object.\nThe database has not been changed.\nThe conversion error was:\n{0}]
353: String msg = s_stringMgr.getString(
354: "dataSetViewerTablePanel.textCantBeConverted",
355: messageBuffer);
356:
357: // display error message and do not update the table
358: JOptionPane
359: .showMessageDialog(
360: this ,
361: msg,
362: // i18n[dataSetViewerTablePanel.conversionError=Conversion Error]
363: s_stringMgr
364: .getString("dataSetViewerTablePanel.conversionError"),
365: JOptionPane.ERROR_MESSAGE);
366: } else {
367: // data converted ok, so update the table
368: super .setValueAt(newValueObject, row, col);
369: }
370: }
371:
372: public void setColumnDefinitions(
373: ColumnDisplayDefinition[] colDefs) {
374: TableColumnModel tcm = createColumnModel(colDefs);
375: setColumnModel(tcm);
376: _typedModel.setHeadings(colDefs);
377:
378: // just in case table is editable, call creator to set up cell editors
379: _creator.setCellEditors(this );
380: _tablePopupMenu.reset();
381: }
382:
383: MyTableModel getTypedModel() {
384: return _typedModel;
385: }
386:
387: /**
388: * Display the popup menu for this component.
389: */
390: private void displayPopupMenu(MouseEvent evt) {
391: _tablePopupMenu.show(evt.getComponent(), evt.getX(), evt
392: .getY());
393: }
394:
395: private TableColumnModel createColumnModel(
396: ColumnDisplayDefinition[] colDefs) {
397: //_colDefs = hdgs;
398: TableColumnModel cm = new DefaultTableColumnModel();
399:
400: _rntc = new RowNumberTableColumn();
401:
402: for (int i = 0; i < colDefs.length; ++i) {
403: ColumnDisplayDefinition colDef = colDefs[i];
404: int colWidth = colDef.getDisplayWidth() * _multiplier;
405: if (colWidth > MAX_COLUMN_WIDTH * _multiplier) {
406: colWidth = MAX_COLUMN_WIDTH * _multiplier;
407: } else if (colWidth < MIN_COLUMN_WIDTH * _multiplier) {
408: colWidth = MIN_COLUMN_WIDTH * _multiplier;
409: }
410:
411: ExtTableColumn col = new ExtTableColumn(i, colWidth,
412: CellComponentFactory
413: .getTableCellRenderer(colDefs[i]), null);
414: col.setHeaderValue(colDef.getLabel());
415: col.setColumnDisplayDefinition(colDef);
416: cm.addColumn(col);
417: }
418:
419: return cm;
420: }
421:
422: void setShowRowNumbers(boolean show) {
423: try {
424: int rowNumColIx = getColumnModel().getColumnIndex(
425: RowNumberTableColumn.ROW_NUMBER_COL_IDENTIFIER);
426: _tableHeader.columnIndexWillBeRemoved(rowNumColIx);
427: } catch (IllegalArgumentException e) {
428: // Column not in model
429: }
430:
431: getColumnModel().removeColumn(_rntc);
432: if (show) {
433: _tableHeader.columnIndexWillBeAdded(0);
434: getColumnModel().addColumn(_rntc);
435: getColumnModel().moveColumn(
436: getColumnModel().getColumnCount() - 1, 0);
437: }
438: }
439:
440: private void createGUI(boolean allowUpdate,
441: IDataSetUpdateableModel updateableObject) {
442: setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
443: setRowSelectionAllowed(false);
444: setColumnSelectionAllowed(false);
445: setCellSelectionEnabled(true);
446: getTableHeader().setResizingAllowed(true);
447: getTableHeader().setReorderingAllowed(true);
448: setAutoCreateColumnsFromModel(false);
449: setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
450: setTableHeader(_tableHeader);
451: _tableHeader.setTable(this );
452:
453: _tablePopupMenu = new TablePopupMenu(allowUpdate,
454: updateableObject, DataSetViewerTablePanel.this );
455: _tablePopupMenu.setTable(this );
456:
457: addMouseListener(new MouseAdapter() {
458: public void mousePressed(MouseEvent evt) {
459: onMousePressed(evt, false);
460: }
461:
462: public void mouseReleased(MouseEvent evt) {
463: onMouseReleased(evt);
464: }
465: });
466:
467: getTableHeader().addMouseListener(new MouseAdapter() {
468: public void mousePressed(MouseEvent evt) {
469: onMousePressed(evt, true);
470: }
471:
472: public void mouseReleased(MouseEvent evt) {
473: onMouseReleased(evt);
474: }
475: });
476:
477: }
478:
479: private void onMouseReleased(MouseEvent evt) {
480: if (evt.isPopupTrigger()) {
481: this .displayPopupMenu(evt);
482: }
483: }
484:
485: private void onMousePressed(MouseEvent evt,
486: boolean clickedOnTableHeader) {
487: if (evt.isPopupTrigger()) {
488: this .displayPopupMenu(evt);
489: } else if (evt.getClickCount() == 2
490: && false == clickedOnTableHeader) {
491: // If this was done when the header was clicked
492: // it prevents MS Excel like adopition of column
493: // sizes by double click. See class ButtonTableHeader.
494:
495: // figure out which column the user clicked on
496: // so we can pass in the right column description
497: Point pt = evt.getPoint();
498: TableColumnModel cm = this .getColumnModel();
499: int columnIndexAtX = cm.getColumnIndexAtX(pt.x);
500:
501: int modelIndex = cm.getColumn(columnIndexAtX)
502: .getModelIndex();
503:
504: if (RowNumberTableColumn.ROW_NUMBER_MODEL_INDEX != modelIndex) {
505: ColumnDisplayDefinition colDefs[] = getColumnDefinitions();
506: CellDataPopup.showDialog(this , colDefs[modelIndex],
507: evt, this ._creator.isTableEditable());
508:
509: }
510: }
511: }
512:
513: public void initColWidths() {
514: _tableHeader.initColWidths();
515: }
516: }
517:
518: /////////////////////////////////////////////////////////////////////////
519: //
520: // Implement the IDataSetTableControls interface,
521: // functions needed to support table operations
522: //
523: // These functions are called from within MyJTable and MyTable to tell
524: // those classes how to operate. The code in these functions will be
525: // different depending on whether the table is read-only or editable.
526: //
527: // The definitions below are for read-only operation. The editable
528: // table panel overrides these functions with the versions that tell the
529: // tables how to set up for editing operations.
530: //
531: //
532: /////////////////////////////////////////////////////////////////////////
533:
534: /**
535: * Tell the table that it is editable. This is called from within
536: * MyTable.isCellEditable(). We do not bother to distinguish between
537: * editable and non-editable cells within the same table.
538: */
539: public boolean isTableEditable() {
540: return false;
541: }
542:
543: /**
544: * Tell the table whether particular columns are editable.
545: */
546: public boolean isColumnEditable(int col, Object originalValue) {
547: return false;
548: }
549:
550: /**
551: * See if a value in a column has been limited in some way and
552: * needs to be re-read before being used for editing.
553: * For read-only tables this may actually return true since we want
554: * to be able to view the entire contents of the cell even if it was not
555: * completely loaded during the initial table setup.
556: */
557: public boolean needToReRead(int col, Object originalValue) {
558: // call the DataType object for this column and have it check the current value
559: return CellComponentFactory.needToReRead(_colDefs[col],
560: originalValue);
561: }
562:
563: /**
564: * Re-read the contents of this cell from the database.
565: * If there is a problem, the message will have a non-zero length after return.
566: */
567: public Object reReadDatum(Object[] values, int col,
568: StringBuffer message) {
569: // call the underlying model to get the whole data, if possible
570: return ((IDataSetUpdateableTableModel) _updateableModel)
571: .reReadDatum(values, _colDefs, col, message);
572: }
573:
574: /**
575: * Function to set up CellEditors. Null for read-only tables.
576: */
577: public void setCellEditors(JTable table) {
578: }
579:
580: /**
581: * Change the data in the permanent store that is represented by the JTable.
582: * Does nothing in read-only table.
583: */
584: public int[] changeUnderlyingValueAt(int row, int col,
585: Object newValue, Object oldValue) {
586: return new int[0]; // underlaying data cannot be changed
587: }
588:
589: /**
590: * Delete a set of rows from the table.
591: * The indexes are the row indexes in the SortableModel.
592: */
593: public void deleteRows(int[] rows) {
594: } // cannot delete rows in read-only table
595:
596: /**
597: * Initiate operations to insert a new row into the table.
598: */
599: public void insertRow() {
600: } // cannot insert row into read-only table
601:
602: //
603: // Begin code related to printing
604: //
605:
606: //
607: // variables used in printing
608: //
609: JTableHeader tableHeader;
610: int[] subTableSplit = null;
611: boolean pageinfoCalculated = false;
612: int totalNumPages = 0;
613: int prevPageIndex = 0;
614: int subPageIndex = 0;
615: int subTableSplitSize = 0;
616: double tableHeightOnFullPage, headerHeight;
617: double pageWidth, pageHeight;
618: int fontHeight, fontDesent;
619: double tableHeight, rowHeight;
620: double scale = 8.0 / 12.0; // default is 12 point, so define font relative to that
621:
622: /**
623: * Print the table contents.
624: * This was copied from a tutorial paper on the Sun Java site:
625: * paper: http://developer.java.sun.com/developer/onlineTraining/Programming/JDCBook/advprint.html
626: * code: http://developer.java.sun.com/developer/onlineTraining/Programming/JDCBook/Code/SalesReport.java
627: */
628: public int print(Graphics g, PageFormat pageFormat, int pageIndex)
629: throws PrinterException {
630:
631: Graphics2D g2 = (Graphics2D) g;
632:
633: // reset each time we start a new print
634: if (pageIndex == 0)
635: pageinfoCalculated = false;
636:
637: if (!pageinfoCalculated) {
638: getPageInfo(g, pageFormat);
639: }
640:
641: g2.setColor(Color.black);
642: if (pageIndex >= totalNumPages) {
643: return NO_SUCH_PAGE;
644: }
645: if (prevPageIndex != pageIndex) {
646: subPageIndex++;
647: if (subPageIndex == subTableSplitSize - 1) {
648: subPageIndex = 0;
649: }
650: }
651:
652: g2.translate(pageFormat.getImageableX(), pageFormat
653: .getImageableY());
654:
655: int rowIndex = pageIndex / (subTableSplitSize - 1);
656:
657: printTablePart(g2, pageFormat, rowIndex, subPageIndex);
658: prevPageIndex = pageIndex;
659:
660: return Printable.PAGE_EXISTS;
661: }
662:
663: /**
664: * Part of print code coped from Sun
665: */
666: public void getPageInfo(Graphics g, PageFormat pageFormat) {
667:
668: subTableSplit = null;
669: subTableSplitSize = 0;
670: subPageIndex = 0;
671: prevPageIndex = 0;
672:
673: fontHeight = (int) (g.getFontMetrics().getHeight() * scale);
674: fontDesent = (int) (g.getFontMetrics().getDescent() * scale);
675:
676: tableHeader = _table.getTableHeader();
677: double headerWidth = tableHeader.getWidth() * scale;
678: headerHeight = tableHeader.getHeight() + _table.getRowMargin()
679: * scale;
680:
681: pageHeight = pageFormat.getImageableHeight();
682: pageWidth = pageFormat.getImageableWidth();
683:
684: // double tableWidth =_table.getColumnModel().getTotalColumnWidth() * scale;
685: tableHeight = _table.getHeight() * scale;
686: rowHeight = _table.getRowHeight() + _table.getRowMargin()
687: * scale;
688:
689: tableHeightOnFullPage = (int) (pageHeight - headerHeight - fontHeight * 2);
690: tableHeightOnFullPage = tableHeightOnFullPage / rowHeight
691: * rowHeight;
692:
693: TableColumnModel tableColumnModel = tableHeader
694: .getColumnModel();
695: int columns = tableColumnModel.getColumnCount();
696: int columnMargin = (int) (tableColumnModel.getColumnMargin() * scale);
697:
698: int[] temp = new int[columns];
699: int columnIndex = 0;
700: temp[0] = 0;
701: int columnWidth;
702: int length = 0;
703: subTableSplitSize = 0;
704: while (columnIndex < columns) {
705:
706: columnWidth = (int) (tableColumnModel
707: .getColumn(columnIndex).getWidth() * scale);
708:
709: if (length + columnWidth + columnMargin > pageWidth) {
710: temp[subTableSplitSize + 1] = temp[subTableSplitSize]
711: + length;
712: length = columnWidth;
713: subTableSplitSize++;
714: } else {
715: length += columnWidth + columnMargin;
716: }
717: columnIndex++;
718: } //while
719:
720: if (length > 0) { // if are more columns left, part page
721: temp[subTableSplitSize + 1] = temp[subTableSplitSize]
722: + length;
723: subTableSplitSize++;
724: }
725:
726: subTableSplitSize++;
727: subTableSplit = new int[subTableSplitSize];
728: for (int i = 0; i < subTableSplitSize; i++) {
729: subTableSplit[i] = temp[i];
730: }
731: totalNumPages = (int) (tableHeight / tableHeightOnFullPage);
732: if (tableHeight % tableHeightOnFullPage >= rowHeight) { // at least 1 more row left
733: totalNumPages++;
734: }
735:
736: totalNumPages *= (subTableSplitSize - 1);
737: pageinfoCalculated = true;
738: }
739:
740: /**
741: * Part of print code coped from Sun
742: */
743: public void printTablePart(Graphics2D g2, PageFormat pageFormat,
744: int rowIndex, int columnIndex) {
745:
746: String pageNumber = "Page: " + (rowIndex + 1);
747: if (subTableSplitSize > 1) {
748: pageNumber += "-" + (columnIndex + 1);
749: }
750:
751: int pageLeft = subTableSplit[columnIndex];
752: int pageRight = subTableSplit[columnIndex + 1];
753:
754: int pageWidth = pageRight - pageLeft;
755:
756: // page number message (in smaller type)
757: g2.setFont(new Font(g2.getFont().getName(), g2.getFont()
758: .getStyle(), 8));
759: g2.drawString(pageNumber, pageWidth / 2 - 35,
760: (int) (pageHeight - fontHeight));
761:
762: double clipHeight = Math.min(tableHeightOnFullPage, tableHeight
763: - rowIndex * tableHeightOnFullPage);
764:
765: g2.translate(-subTableSplit[columnIndex], 0);
766: g2.setClip(pageLeft, 0, pageWidth, (int) headerHeight);
767:
768: g2.scale(scale, scale);
769: tableHeader.paint(g2); // draw the header on every page
770: g2.scale(1 / scale, 1 / scale);
771: g2.translate(0, headerHeight);
772: g2.translate(0, -tableHeightOnFullPage * rowIndex);
773:
774: // cut table image and draw on the page
775:
776: g2.setClip(pageLeft, (int) tableHeightOnFullPage * rowIndex,
777: pageWidth, (int) clipHeight);
778: g2.scale(scale, scale);
779: _table.paint(g2);
780: g2.scale(1 / scale, 1 / scale);
781:
782: double pageTop = tableHeightOnFullPage * rowIndex
783: - headerHeight;
784: double pageBottom = pageTop + clipHeight + headerHeight;
785: g2.drawRect(pageLeft, (int) pageTop, pageWidth,
786: (int) (clipHeight + headerHeight));
787: }
788:
789: //
790: // End of code related to printing
791: //
792:
793: }
|