0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: /*
0043: * TableItem.java
0044: *
0045: * Created on July 26, 2005, 1:01 PM
0046: *
0047: */
0048:
0049: package org.netbeans.microedition.lcdui;
0050:
0051: import javax.microedition.lcdui.Canvas;
0052: import javax.microedition.lcdui.CustomItem;
0053: import javax.microedition.lcdui.Display;
0054: import javax.microedition.lcdui.Displayable;
0055: import javax.microedition.lcdui.Font;
0056: import javax.microedition.lcdui.Graphics;
0057: import org.netbeans.microedition.lcdui.laf.ColorSchema;
0058: import org.netbeans.microedition.lcdui.laf.SystemColorSchema;
0059:
0060: /**
0061: * An item that visualizes a table from <code>TableModel</code>.
0062: * <p/>
0063: * The table can be larger than a screen in both directions - user can use
0064: * a cursor to scroll in all directions.
0065: * @author breh
0066: */
0067: public class TableItem extends CustomItem implements TableModelListener {
0068:
0069: private static final boolean DEBUG = false;
0070:
0071: private int tableRows = 0; // count of table rows (from model)
0072: private int tableCols = 0; // count of table columns (from model)
0073:
0074: private String title = null; // title of the table
0075:
0076: private boolean borders = true; // is using borders?
0077: private boolean usingHeaders = false; // is using usingHeaders (column names)?
0078:
0079: private final Display display; // /display used to get system colors
0080:
0081: private static final Font STATIC_TEXT_FONT = Font
0082: .getFont(Font.FONT_STATIC_TEXT); // standard font for items to draw text
0083:
0084: // default font for title - using standard font with Bold style
0085: private static final Font DEFAULT_TITLE_FONT = Font.getFont(
0086: STATIC_TEXT_FONT.getFace(), STATIC_TEXT_FONT.getStyle()
0087: | Font.STYLE_BOLD, STATIC_TEXT_FONT.getSize());;
0088:
0089: // default font for title - using standard font with Bold style
0090: private static final Font DEFAULT_HEADERS_FONT = Font.getFont(
0091: STATIC_TEXT_FONT.getFace(), STATIC_TEXT_FONT.getStyle()
0092: | Font.STYLE_BOLD, STATIC_TEXT_FONT.getSize());;
0093:
0094: // values are using standard font
0095: private static final Font DEFAULT_VALUES_FONT = STATIC_TEXT_FONT;
0096:
0097: private Font titleFont = DEFAULT_TITLE_FONT; // titleFont
0098: private Font headersFont = DEFAULT_HEADERS_FONT; // usingHeaders (column names) font
0099: private Font valuesFont = DEFAULT_VALUES_FONT; // values font
0100:
0101: private TableModel model; // model
0102:
0103: private int[] colWidths; // array of individual column widths
0104: private int[] rowHeights; // array of individual row height - currently using the same height for all
0105: private int totalColWidth; // total width of all columns
0106:
0107: private int defaultCellWidth, defaultCellHeight; // default cell width/heights
0108:
0109: private static final int CELL_PADDING = 2; // size text cell padding
0110: private static final int DOUBLE_CELL_PADDING = 2 * CELL_PADDING; // double cell padding helper for computation
0111: private static final int BORDER_LINE_WIDTH = 1; // size of border line
0112:
0113: //private boolean alwaysDrawTitle = false; // always draw title even the table is scrolled // not used in this release
0114: //private boolean alwaysDrawHeaders = true; // always draw usingHeaders (column names) // not used in this release
0115:
0116: private boolean cursorOn = false; // should cursor be drawn?
0117: private int cursorCellX, cursorCellY; // coordinates of cursor
0118:
0119: private int viewCellX = 0; // X value of a cell which is visible on the left
0120: private int viewCellX2 = 0; // X value of a cell which is visible on the right
0121:
0122: private boolean tableFitsHorizontally = true; // does the whole table fit on the screen?
0123:
0124: private boolean firstPaint = true;
0125:
0126: private int sizeWidth = 0; // current size assigned to this item - some implementations (Nokia) still
0127: private int sizeHeight = 0; // keeps calling sizeChanged() with the same size, so I keep these values
0128: // to not call repaint() when it is not neccessary
0129:
0130: private ColorSchema colorSchema; // color schema in use
0131:
0132: /**
0133: * Creates a new instance of <code>TableItem</code> without any model.
0134: * @param display non-null display parameter.
0135: *
0136: * @param label label for the item
0137: * @throws java.lang.IllegalArgumentException if the display parameter is null
0138: */
0139: public TableItem(Display display, String label)
0140: throws IllegalArgumentException {
0141: this (display, label, null);
0142: }
0143:
0144: /**
0145: * Creates a new instance of <code>TableItem</code> with a model.
0146: *
0147: * @param display non-null display parameter.
0148: * @param label label for the item
0149: * @param model a <code>TableModel</code> to be visualized by this item
0150: * @throws java.lang.IllegalArgumentException if the display parameter is null
0151: */
0152: public TableItem(Display display, String label, TableModel model)
0153: throws IllegalArgumentException {
0154: this (display, label, model, null);
0155: }
0156:
0157: /**
0158: * Creates a new instance of <code>TableItem</code> with a model.
0159: *
0160: * @param display non-null display parameter.
0161: * @param label label for the item
0162: * @param model a <code>TableModel</code> to be visualized by this item
0163: * @param colorSchema a color schema to be used. If set to null, SystemCOlorSchema will be used
0164: * @throws java.lang.IllegalArgumentException if the display parameter is null
0165: */
0166: public TableItem(Display display, String label, TableModel model,
0167: ColorSchema colorSchema) throws IllegalArgumentException {
0168: super (label);
0169:
0170: if (display == null)
0171: throw new IllegalArgumentException(
0172: "display parameter cannot be null");
0173: this .display = display;
0174:
0175: updateDefaultCellSize();
0176: if (model != null) {
0177: setModel(model);
0178: } else {
0179: recomputeModelValues();
0180: }
0181: setColorSchemaImpl(display, colorSchema);
0182:
0183: }
0184:
0185: /**
0186: * Sets color schema to be used with this component. If set to null
0187: * SystemColorSchema will be used
0188: */
0189: public void setColorSchema(ColorSchema colorSchema) {
0190: setColorSchemaImpl(display, colorSchema);
0191: repaint();
0192: }
0193:
0194: /**
0195: * Gets color schema currently in use
0196: */
0197: public ColorSchema getColorSchema() {
0198: return colorSchema;
0199: }
0200:
0201: /**
0202: * Gets title of the table.
0203: * @return title string or null if there is no title defined
0204: */
0205: public String getTitle() {
0206: return title;
0207: }
0208:
0209: /**
0210: * Sets title of the table. The difference between title and label
0211: * specified in the constructor is, that the title appears in the table (it
0212: * is surrounded by the borders). Title can be null if no title should
0213: * be shown.
0214: * <p/>
0215: * The table is automatically repainted when a new title is set.
0216: * @param tableTitle title for the table. Can be null
0217: */
0218: public void setTitle(String tableTitle) {
0219: if (this .title != tableTitle) {
0220: this .title = tableTitle;
0221: repaint();
0222: }
0223: }
0224:
0225: /**
0226: * Should the table use borders
0227: * @return true if the table will be painted with borders. False if it will be
0228: * borderless.
0229: */
0230: public boolean isBorders() {
0231: return borders;
0232: }
0233:
0234: /**
0235: * Sets state whether the table should be visualized with or without borders.
0236: * <p/>
0237: * The table is automatically repainted when the state is changed.
0238: * @param showBorders true if the borders should be painted, false otherwise
0239: */
0240: public void setBorders(boolean showBorders) {
0241: if (this .borders != showBorders) {
0242: this .borders = showBorders;
0243: repaint();
0244: }
0245: }
0246:
0247: /**
0248: * Sets a new model to the table. The table gets automatically repainted
0249: * accordingly to a new model.
0250: * @param model a new model to be visualized - cannot be null
0251: * @throws java.lang.IllegalArgumentException if the model parameter is null
0252: */
0253: public void setModel(TableModel model)
0254: throws IllegalArgumentException {
0255: if (this .model != null) {
0256: this .model.removeTableModelListener(this );
0257: }
0258: if (model == null)
0259: throw new IllegalArgumentException(
0260: "model parameter cannot be null");
0261: if (model != null) {
0262: if (model.getRowCount() < 0)
0263: throw new IllegalArgumentException(
0264: "model cannot have negative number of rows");
0265: if (model.getColumnCount() < 0)
0266: throw new IllegalArgumentException(
0267: "model cannot have negative number of cols");
0268: model.addTableModelListener(this );
0269: }
0270: this .model = model;
0271: recomputeModelValues();
0272: invalidateTable();
0273: }
0274:
0275: /**
0276: * Gets font used to paint the table title
0277: * @return title font
0278: */
0279: public Font getTitleFont() {
0280: return titleFont;
0281: }
0282:
0283: /**
0284: * Sets the font to be used to paint title. If null is specified,
0285: * the default font (bold version of static text font) will be used.
0286: * <p/>
0287: * When the font changes, the table gets automatically repainted.
0288: * @param titleFont font to be used for painting table title, might be null.
0289: */
0290: public void setTitleFont(Font titleFont) {
0291: if (this .titleFont != titleFont) {
0292: if (titleFont != null) {
0293: this .titleFont = titleFont;
0294: } else {
0295: this .titleFont = DEFAULT_TITLE_FONT;
0296: }
0297: repaint();
0298: }
0299: }
0300:
0301: /**
0302: * Gets font used to paint the table cell values
0303: * @return values font
0304: */
0305: public Font getValuesFont() {
0306: return valuesFont;
0307: }
0308:
0309: /**
0310: * Sets the font to be used to paint title. If null is specified,
0311: * the default font (static text font) will be used.
0312: * <p/>
0313: * When the font changes, the table gets automatically repainted.
0314: * @param valuesFont font used for painting values, might be null
0315: */
0316: public void setValuesFont(Font valuesFont) {
0317: if (this .valuesFont != valuesFont) {
0318: if (valuesFont != null) {
0319: this .valuesFont = valuesFont;
0320: } else {
0321: this .valuesFont = DEFAULT_VALUES_FONT;
0322: }
0323: updateDefaultCellSize();
0324: recomputeModelValues();
0325: repaint();
0326: }
0327: }
0328:
0329: /**
0330: * Gets font used to paint the column names (headers) of the table
0331: * @return headers font
0332: */
0333: public Font getHeadersFont() {
0334: return headersFont;
0335: }
0336:
0337: /**
0338: * Sets the font to be used to paint title. If null is specified,
0339: * the default font (bold static text font) will be used.
0340: * <p/>
0341: * When the font changes, the table gets automatically repainted.
0342: * @param headersFont font used for painting column names (headers), might be null
0343: */
0344: public void setHeadersFont(Font headersFont) {
0345: if (this .headersFont != headersFont) {
0346: if (headersFont != null) {
0347: this .headersFont = headersFont;
0348: } else {
0349: this .headersFont = DEFAULT_HEADERS_FONT;
0350: }
0351: repaint();
0352: }
0353: }
0354:
0355: /**
0356: * Gets the row position of the cursor in the table.
0357: * @return selected cell row
0358: */
0359: public int getSelectedCellRow() {
0360: return cursorCellY;
0361: }
0362:
0363: /**
0364: * Gets the column position of the cursor in the table.
0365: * @return selected cell column
0366: */
0367: public int getSelectedCellColumn() {
0368: return cursorCellX;
0369: }
0370:
0371: /*
0372: private static Font getSafeFont(Font font) {
0373: return font == null ? Font.getDefaultFont() : font;
0374: }*/
0375:
0376: /**
0377: * implementation of the abstract method
0378: * @return minimal content height
0379: */
0380: protected int getMinContentHeight() {
0381: int sum = (tableRows * (defaultCellHeight + BORDER_LINE_WIDTH))
0382: + BORDER_LINE_WIDTH;
0383: if (title != null) {
0384: sum += getTitleFont().getHeight() + DOUBLE_CELL_PADDING
0385: + BORDER_LINE_WIDTH;
0386: }
0387: if (usingHeaders) {
0388: sum += getHeadersFont().getHeight() + DOUBLE_CELL_PADDING
0389: + BORDER_LINE_WIDTH;
0390: }
0391: /*
0392: if (getLabel() != null) { // this is rather hack - I don't know the size of the label !!!
0393: sum += Font.getDefaultFont().getHeight();
0394: }*/
0395: return sum;
0396: }
0397:
0398: protected int getMinContentWidth() {
0399: if (DEBUG)
0400: System.out
0401: .println("!!!!!!!!!!!!!!!!!! TableItem.getMinContentWidth(): XXXX returing -1");
0402: return -1;
0403: }
0404:
0405: /**
0406: * implementation of the abstract method
0407: * @param width
0408: * @return preferred contnent height
0409: */
0410: protected int getPrefContentHeight(int width) {
0411: if (DEBUG)
0412: System.out
0413: .println("TableItem.getPrefContentHeight: tentative width="
0414: + width);
0415: return getMinContentHeight();
0416: }
0417:
0418: /**
0419: * implementation of the abstract method
0420: * @param height
0421: * @return preferred content width
0422: */
0423: protected int getPrefContentWidth(int height) {
0424: if (DEBUG)
0425: System.out
0426: .println("WTableItem.getPrefContentWidth: tentative heigth="
0427: + height);
0428: final Displayable currentDisplayable = display.getCurrent();
0429: final int displayWidth = currentDisplayable == null ? -1
0430: : currentDisplayable.getWidth();
0431: if (DEBUG)
0432: System.out
0433: .println("TableItem.getPrefContentWidth(): Current display width = "
0434: + displayWidth);
0435: int sum = colWidths.length * BORDER_LINE_WIDTH
0436: + BORDER_LINE_WIDTH;
0437: int result = 0;
0438: for (int i = 0; i < colWidths.length; i++) {
0439: sum += colWidths[i];
0440: }
0441: result = sum;
0442: if (title != null) {
0443: int titleWidth = getTitleFont().stringWidth(title);
0444: if (titleWidth > result) {
0445: result = titleWidth + DOUBLE_CELL_PADDING;
0446: }
0447: }
0448: /*
0449: if (getLabel() != null) { // this is rather hack - I don't know the size of the label !!!
0450: int labelWidth = Font.getDefaultFont().stringWidth(getLabel());
0451: if (labelWidth > result) {
0452: result = labelWidth;
0453: }
0454: }
0455: if (DEBUG) System.out.println("TableItem.getPrefContentWidth(): label result= "+result);
0456: */
0457: // I should never return a width larger than the display width
0458: if ((displayWidth > 0) && (result > displayWidth)) {
0459: result = displayWidth;
0460: }
0461: if (DEBUG)
0462: System.out
0463: .println("TableItem.getPrefContentWidth(): returning preffered width = "
0464: + result);
0465: return result;
0466: }
0467:
0468: /**
0469: * implementation of the abstract method
0470: * @param g
0471: * @param width
0472: * @param height
0473: */
0474: protected void paint(Graphics g, int width, int height) {
0475:
0476: //if (doPaint) {
0477: firstPaint = false;
0478:
0479: if (DEBUG)
0480: System.out.println("\n@@@@@@@@@@@@@@ paint: width=" + width
0481: + " height=" + height + "" + "::: " + this );
0482: if (DEBUG)
0483: System.out.println("@@@@@@@@@@@@@@ paint: Clip: X="
0484: + g.getClipX() + " Y=" + g.getClipY() + " W="
0485: + g.getClipWidth() + " H=" + g.getClipHeight());
0486:
0487: final int paintWidth = sizeWidth > 0 ? sizeWidth : width;
0488: final int paintHeight = sizeHeight > 0 ? sizeHeight : height;
0489:
0490: tableFitsHorizontally = totalColWidth <= paintWidth; // does the table horizontally fit to the given drawing area
0491: if (DEBUG)
0492: System.out.println("@@@@@@@@@@@@@@ paint: totalColWidth = "
0493: + totalColWidth + ", paintWidth=" + paintWidth);
0494:
0495: boolean rightmostColumnFullyVisible = true; // is the table scrolled to the right (i.e. the rightmost column is fully visible?)
0496:
0497: int currentColor = g.getColor();
0498:
0499: // paint the background based on color schema
0500: getColorSchema().paintBackground(g, false);
0501: // clear the area
0502: //g.setColor(getColorSchema().getColor(Display.COLOR_BACKGROUND));
0503: //g.fillRect(g.getClipX(), g.getClipY(), g.getClipWidth(), g.getClipHeight());
0504: //g.fillRect(0, 0, width, height);
0505:
0506: int hy = paintHeight - 1; // actual height ?
0507: int wx = paintWidth - 1; // actual width ?
0508:
0509: // get height of title
0510: int titleHeight = 0;
0511: if (title != null) {
0512: titleHeight = getTitleFont().getHeight()
0513: + DOUBLE_CELL_PADDING; // title height
0514: }
0515:
0516: // get height of headers
0517: int headersHeight = 0;
0518: if (usingHeaders) {
0519: headersHeight = getHeadersFont().getHeight()
0520: + DOUBLE_CELL_PADDING; // headers height
0521: }
0522:
0523: int viewWidth = wx; // current visible width of the table
0524:
0525: int actualTableWidth = paintWidth; // actual table width (based on cell computation), used to draw borders
0526:
0527: if ((model != null) && (tableRows != 0) && (tableCols != 0)) {
0528:
0529: // update visible width of the table based on columns to be drawn
0530: //viewWidth = wx;
0531: if (viewCellX2 < (tableCols - 1)) {
0532: viewWidth = wx;
0533: } else {
0534: viewWidth = 0;
0535: for (int i = viewCellX; (i <= viewCellX2)
0536: && (i < tableCols); i++) {
0537: viewWidth += colWidths[i];
0538: }
0539: if (viewWidth > wx) {
0540: viewWidth = wx;
0541: }
0542: }
0543:
0544: // paint cursor
0545: if (cursorOn) {
0546: final int x = getCursorX();
0547: int y = getCursorY() + BORDER_LINE_WIDTH;
0548:
0549: // add title height if to be drawn
0550: if (title != null) {
0551: y += titleHeight + BORDER_LINE_WIDTH;
0552: }
0553: // add headers height if to be drawn
0554: if (usingHeaders) {
0555: y += headersHeight + BORDER_LINE_WIDTH;
0556: }
0557:
0558: final int w = colWidths[cursorCellX];
0559: final int h = defaultCellHeight;
0560: //g.setColor(cursorColor);
0561: // draw cursor ...
0562: g.setColor(getColorSchema().getColor(
0563: Display.COLOR_HIGHLIGHTED_BACKGROUND));
0564: g.fillRect(x, y, w, h);
0565: }
0566:
0567: int y = 0; // y coordinate to be used to draw headers/values
0568: // add title height if to be drawn
0569: if (title != null) {
0570: y += titleHeight + BORDER_LINE_WIDTH;
0571: }
0572: // add headers height if to be drawn
0573:
0574: // draw headers
0575: if (usingHeaders) {
0576: g.setColor(getColorSchema().getColor(
0577: Display.COLOR_FOREGROUND));
0578: g.setFont(getHeadersFont());
0579:
0580: int x = BORDER_LINE_WIDTH;
0581: final int gy = y + CELL_PADDING + BORDER_LINE_WIDTH; // actual y used to be draw the text (icludes padding)
0582: for (int j = viewCellX; j < model.getColumnCount(); j++) {
0583: viewCellX2 = j;
0584:
0585: final Object value = model.getColumnName(j);
0586: final int colWidth = colWidths[j];
0587:
0588: if (value != null) {
0589: g.drawString(value.toString(),
0590: x + colWidth / 2, gy, Graphics.TOP
0591: | Graphics.HCENTER);
0592: }
0593: x += colWidth + BORDER_LINE_WIDTH;
0594: if (x > paintWidth) {
0595: rightmostColumnFullyVisible = false;
0596: break;
0597: }
0598: }
0599: y += headersHeight + BORDER_LINE_WIDTH;
0600: }
0601:
0602: // draw values
0603: g.setColor(getColorSchema().getColor(
0604: Display.COLOR_FOREGROUND));
0605: g.setFont(getValuesFont());
0606:
0607: for (int i = 0; (i < model.getRowCount()); i++) {
0608:
0609: int x = BORDER_LINE_WIDTH + CELL_PADDING;
0610: final int gy = y + CELL_PADDING + BORDER_LINE_WIDTH; // actual y used to be draw the text (icludes padding)
0611:
0612: for (int j = viewCellX; j < model.getColumnCount(); j++) {
0613: viewCellX2 = j;
0614: Object value = model.getValue(j, i);
0615: if (value != null) {
0616: if (cursorOn && (j == cursorCellX)
0617: && (i == cursorCellY)) {
0618: g
0619: .setColor(getColorSchema()
0620: .getColor(
0621: Display.COLOR_HIGHLIGHTED_FOREGROUND));
0622: g.drawString(value.toString(), x, gy,
0623: Graphics.TOP | Graphics.LEFT);
0624: g.setColor(getColorSchema().getColor(
0625: Display.COLOR_FOREGROUND));
0626: } else {
0627: g.drawString(value.toString(), x, gy,
0628: Graphics.TOP | Graphics.LEFT);
0629: }
0630: }
0631: x += colWidths[j] + BORDER_LINE_WIDTH;
0632: if (x > width) {
0633: rightmostColumnFullyVisible = false;
0634: break;
0635: }
0636: }
0637: y += defaultCellHeight + BORDER_LINE_WIDTH;
0638: }
0639:
0640: // finally draw borders (if applicable)
0641: if (isBorders()) {
0642: g.setColor(getColorSchema().getColor(
0643: Display.COLOR_BORDER));
0644: int currentStrokeStyle = g.getStrokeStyle();
0645: g.setStrokeStyle(display.getBorderStyle(false));
0646:
0647: y = titleHeight;
0648:
0649: int totalTableHeight = tableRows
0650: * (defaultCellHeight + BORDER_LINE_WIDTH)
0651: + titleHeight; // total totalTableHeight
0652: if (usingHeaders) {
0653: totalTableHeight += headersHeight
0654: + BORDER_LINE_WIDTH; // add it to total table height
0655: }
0656:
0657: // vertical lines
0658: int x = 0;
0659: for (int i = viewCellX; (i < tableCols) && (x < width); i++) {
0660: x += colWidths[i] + BORDER_LINE_WIDTH;
0661: g.drawLine(x, titleHeight, x, totalTableHeight);
0662: actualTableWidth = x; // getting value of the leftmost table line - out actualTableWidth
0663: }
0664: if ((titleHeight > 0) && rightmostColumnFullyVisible) {
0665: g.drawLine(x, 0, x, titleHeight);
0666: }
0667:
0668: // horizontal lines
0669:
0670: // first line on the top
0671: g.drawLine(0, 0, actualTableWidth, 0);
0672:
0673: // draw header line
0674: if (usingHeaders) {
0675: g.drawLine(0, y, actualTableWidth, y);
0676: y += headersHeight + BORDER_LINE_WIDTH;
0677: }
0678:
0679: // draw value lines
0680: for (int i = 0; (i <= tableRows)
0681: && (y <= totalTableHeight); i++, y += defaultCellHeight
0682: + BORDER_LINE_WIDTH) {
0683: g.drawLine(0, y, actualTableWidth, y);
0684: }
0685:
0686: // draw the remaining left and right line
0687: g.drawLine(0, 0, 0, totalTableHeight);
0688: g.setStrokeStyle(currentStrokeStyle);
0689: }
0690: }
0691:
0692: // draw title
0693: if (title != null) {
0694: g.setColor(getColorSchema().getColor(
0695: Display.COLOR_FOREGROUND));
0696: g.setFont(titleFont);
0697: // first is table title
0698: g.drawString(title, actualTableWidth / 2, BORDER_LINE_WIDTH
0699: + CELL_PADDING, Graphics.TOP | Graphics.HCENTER);
0700: int half = titleHeight / 2;
0701: if (viewCellX > 0) {
0702: // draw triangle on the left
0703: g.drawLine(2, half, 5, half - 2);
0704: g.drawLine(2, half, 5, half + 2);
0705: }
0706: if (!rightmostColumnFullyVisible) {
0707: // draw triangle on the right
0708: int rx = actualTableWidth - 2;
0709: g.drawLine(rx, half, rx - 3, half - 2);
0710: g.drawLine(rx, half, rx - 3, half + 2);
0711: }
0712: }
0713:
0714: g.setColor(currentColor);
0715: /*} else {
0716: if (DEBUG) System.out.println("\n@@@@@@@@@@@@@@ NO PAINT: width="+width+" height="+height+"");
0717: if (DEBUG) System.out.println("@@@@@@@@@@@@@@ NO PAINT : Clip: X="+g.getClipX()+" Y="+g.getClipY()+" W="+g.getClipWidth()+" H="+g.getClipHeight());
0718: }*/
0719: }
0720:
0721: /**
0722: * implementation of the abstract method - if the item size has changed, simply repaint the table
0723: * @param w
0724: * @param h
0725: */
0726: protected void sizeChanged(int w, int h) {
0727: if (DEBUG)
0728: System.out.println("^^^^^^^^^^ sizeChanged : w=" + w
0729: + " h=" + h + " ::: " + this );
0730: if ((!firstPaint) && (w > 0) && (h > 0) && (w != sizeWidth)
0731: && (h != sizeHeight)) {
0732: sizeWidth = w;
0733: sizeHeight = h;
0734: //repaint();
0735: }
0736: }
0737:
0738: /**
0739: * implementation of the abstract method
0740: */
0741: protected boolean traverse(int dir, int viewportWidth,
0742: int viewportHeight, int[] visRect_inout) {
0743: if (DEBUG)
0744: System.out
0745: .println("\n****************************\nTraversal occured: dir = "
0746: + dir + " ::: " + this );
0747: if (DEBUG)
0748: System.out.println("traverse:viewportWidth = "
0749: + viewportWidth + ", viewportHeight = "
0750: + viewportHeight);
0751: if (DEBUG)
0752: System.out.println("traverse:visRect = ["
0753: + visRect_inout[0] + "," + visRect_inout[1] + ","
0754: + visRect_inout[2] + "," + visRect_inout[3] + "]");
0755: if (DEBUG)
0756: System.out.println("traverse: cursorCellX = " + cursorCellX
0757: + ", cursorCellY = " + cursorCellY);
0758: final int visibleWidth = visRect_inout[2];
0759: final int visibleHeight = visRect_inout[3];
0760:
0761: if (model == null) {
0762: // model is not set - do not traverse ...
0763: if (DEBUG)
0764: System.out
0765: .println("traverse: model is not set - returning\n\n");
0766: return false;
0767: } // else
0768: boolean retValue = false; // what should be returned - traversal occured (true), did not occured (false)
0769: boolean repaint = false; // should be the component repainted?
0770: if (cursorOn == false) {
0771: if (DEBUG)
0772: System.out
0773: .println("traverse: cusorOn was false, entering item ..., dir = "
0774: + dir);
0775: //cursorCellX = 0;
0776: if (dir == Canvas.UP) {
0777: cursorCellY = tableRows - 1;
0778: } else if (dir == Canvas.DOWN) {
0779: cursorCellY = 0;
0780: } else if (dir == Canvas.RIGHT) {
0781: cursorCellY = 0;
0782: cursorCellX = 0;
0783: } else if (dir == Canvas.LEFT) {
0784: cursorCellY = tableRows - 1;
0785: cursorCellX = tableCols - 1;
0786: viewCellX2 = cursorCellX;
0787: } else {
0788: // this can happen only when a traverseIn was called
0789: // after traverseOut
0790: // do not move cursor - simply return back to the item and repaint
0791: }
0792: cursorOn = true; // cursor is on !!!
0793: retValue = true;
0794: repaint = true;
0795: } else {
0796: // move cursor
0797: if (dir == Canvas.UP) {
0798: cursorCellY--;
0799: } else if (dir == Canvas.DOWN) {
0800: cursorCellY++;
0801: } else if (dir == Canvas.LEFT) {
0802: cursorCellX--;
0803: } else if (dir == Canvas.RIGHT) {
0804: cursorCellX++;
0805: }
0806: // now check if we are inbounds
0807: if (cursorCellX < 0) {
0808: cursorCellX = 0;
0809: retValue = true;
0810: repaint = false;
0811: } else if (cursorCellX >= tableCols) {
0812: cursorCellX = tableCols - 1;
0813: retValue = true;
0814: repaint = false;
0815: }
0816: if ((cursorCellY >= 0) && (cursorCellY < tableRows)) {
0817: if (DEBUG)
0818: System.out.println("traverse: cursorY in bounds");
0819: retValue = true;
0820: repaint = true;
0821: } else {
0822: if (DEBUG)
0823: System.out
0824: .println("traverse: cursorY out of bounds");
0825: retValue = false;
0826: repaint = false;
0827: if (cursorCellY < 0) {
0828: cursorCellY = 0;
0829: } else {
0830: cursorCellY = tableRows - 1;
0831: }
0832: }
0833: }
0834:
0835: int cursorY = getCursorY();
0836: int cursorHeight = defaultCellHeight;
0837: int headersHeight = 0;
0838: if (title != null) {
0839: headersHeight += getTitleFont().getHeight()
0840: + DOUBLE_CELL_PADDING + BORDER_LINE_WIDTH;
0841: }
0842: // add headers height if to be drawn
0843: if (usingHeaders) {
0844: headersHeight += getHeadersFont().getHeight()
0845: + DOUBLE_CELL_PADDING + BORDER_LINE_WIDTH;
0846: }
0847:
0848: //if ((cursorY + cursorHeight + headersHeight) > viewportHeight) {
0849: // y
0850: visRect_inout[1] = cursorY + headersHeight + 2
0851: * BORDER_LINE_WIDTH;
0852: visRect_inout[3] = defaultCellHeight;
0853: // x
0854: visRect_inout[0] = getCursorX();
0855: visRect_inout[2] = defaultCellWidth;
0856: /*} else {
0857: //visRect_inout[1] = 0;
0858: visRect_inout[0] = getCursorX();
0859: visRect_inout[2] = defaultCellWidth;
0860: //visRect_inout[3] = viewportHeight;
0861: visRect_inout[3] = defaultCellHeight;
0862: }*/
0863:
0864: /*
0865: visRect_inout[1] = getCursorY() + defaultCellHeight;
0866: int horizontalShift = 0;
0867: if (title != null) {
0868: horizontalShift = getTitleFont().getHeight() + CELL_PADDING + BORDER_LINE_WIDTH;
0869: }
0870: if (usingHeaders) {
0871: horizontalShift += getHeadersFont().getHeight() + CELL_PADDING + BORDER_LINE_WIDTH;
0872: }
0873: if (dir == Canvas.UP) {
0874: visRect_inout[1] -= horizontalShift;
0875: } else if (dir == Canvas.DOWN) {
0876: visRect_inout[1] += horizontalShift;
0877: }
0878:
0879: // visRect_inout[2] = colWidths[cursorCellX]; // we don't care about X
0880: visRect_inout[3] = defaultCellHeight;
0881: */
0882: if (DEBUG)
0883: System.out.println("traverse: tableFits = "
0884: + tableFitsHorizontally);
0885: if (!tableFitsHorizontally) {
0886: repaint = true;
0887: if (cursorCellX >= viewCellX2) {
0888: int sum = BORDER_LINE_WIDTH + CELL_PADDING;
0889: ;
0890: int i = viewCellX2;
0891: while ((sum <= visibleWidth) && (i >= 0)) {
0892: sum += colWidths[i] + BORDER_LINE_WIDTH;
0893: i--;
0894: }
0895: viewCellX = i + 2;
0896: // if (viewCellX >= tableCols) {
0897: // viewCellX = tableCols - 1;
0898: // }
0899:
0900: if (viewCellX > viewCellX2) {
0901: viewCellX = cursorCellX;
0902: }
0903: } else if (cursorCellX < viewCellX) {
0904: viewCellX--;
0905: }
0906: }
0907: if (repaint) {
0908: repaint();
0909: }
0910: if (DEBUG)
0911: System.out.println("traverse: returning: " + retValue
0912: + ", cursorCellX = " + cursorCellX
0913: + ", cursorCellY = " + cursorCellY);
0914: if (DEBUG)
0915: System.out.println("traverse: returning visRect = ["
0916: + visRect_inout[0] + "," + visRect_inout[1] + ","
0917: + visRect_inout[2] + "," + visRect_inout[3]
0918: + "]+\n\n");
0919: return retValue;
0920: }
0921:
0922: /**
0923: * implementation of the abstract method
0924: */
0925: protected void traverseOut() {
0926: if (DEBUG)
0927: System.out
0928: .println("--------- traverseOut !!! ::: " + this );
0929: super .traverseOut();
0930: cursorOn = false;
0931: repaint();
0932: }
0933:
0934: /*****
0935: *
0936: * private methods
0937: *
0938: ******/
0939:
0940: private int getCursorX() {
0941: int x = BORDER_LINE_WIDTH;
0942: for (int i = viewCellX; i < cursorCellX; i++) {
0943: x += colWidths[i] + BORDER_LINE_WIDTH;
0944: }
0945: return x;
0946: }
0947:
0948: private int getCursorY() {
0949: return cursorCellY * (defaultCellHeight + BORDER_LINE_WIDTH);
0950: }
0951:
0952: /**
0953: * recomputes cell sizes based on set data and fonts
0954: */
0955: private void recomputeModelValues() {
0956: if (model != null) {
0957: tableRows = model.getRowCount();
0958: tableCols = model.getColumnCount();
0959: } else {
0960: tableCols = 0;
0961: tableRows = 0;
0962: usingHeaders = false;
0963: }
0964: // cells
0965: colWidths = new int[tableCols];
0966: for (int i = 0; i < tableCols; i++) {
0967: colWidths[i] = defaultCellWidth;
0968: }
0969: if (model != null) {
0970: // values
0971: final int columnCount = model.getColumnCount();
0972: final int rowCount = model.getRowCount();
0973: for (int i = 0; i < columnCount; i++) {
0974: for (int j = 0; j < rowCount; j++) {
0975: Object value = model.getValue(i, j);
0976: if (value != null) {
0977: int width = getValuesFont().stringWidth(
0978: value.toString())
0979: + DOUBLE_CELL_PADDING;
0980: if (width > colWidths[i]) {
0981: colWidths[i] = width;
0982: }
0983: }
0984: }
0985: }
0986: // column header (they might be bigger)
0987: usingHeaders = model.isUsingHeaders();
0988: if (model.isUsingHeaders()) {
0989: for (int i = 0; i < columnCount; i++) {
0990: String columnName = model.getColumnName(i);
0991: if (columnName != null) {
0992: int width = getHeadersFont().stringWidth(
0993: columnName.toString())
0994: + DOUBLE_CELL_PADDING;
0995: if (width > colWidths[i]) {
0996: colWidths[i] = width;
0997: }
0998: }
0999: }
1000: }
1001: }
1002:
1003: // compute total column widthj
1004: totalColWidth = BORDER_LINE_WIDTH;
1005: for (int i = 0; i < colWidths.length; i++) {
1006: totalColWidth += colWidths[i] + BORDER_LINE_WIDTH;
1007: }
1008:
1009: }
1010:
1011: /*
1012: * recomputes base defaultCellWidth, defaultCellHeight based on current values font
1013: */
1014: private void updateDefaultCellSize() {
1015: defaultCellWidth = getValuesFont().stringWidth("X")
1016: + DOUBLE_CELL_PADDING;
1017: defaultCellHeight = getValuesFont().getHeight()
1018: + DOUBLE_CELL_PADDING;
1019: }
1020:
1021: /**
1022: * Sets color schema. If null, creates a new SystemColorSchema based on the display
1023: */
1024: private void setColorSchemaImpl(Display display,
1025: ColorSchema colorSchema) {
1026: if (colorSchema != null) {
1027: this .colorSchema = colorSchema;
1028: } else {
1029: this .colorSchema = SystemColorSchema.getForDisplay(display);
1030: }
1031: }
1032:
1033: /**
1034: * Workaround for SE phones - they throw NPE from invalidate() method call,
1035: * when the CustomItem component hasn't been added to the form, therefore
1036: * it does not have any sizes set.
1037: */
1038: private void invalidateTable() {
1039: if ((sizeWidth > 0) && (sizeHeight > 0)) {
1040: invalidate();
1041: }
1042: }
1043:
1044: /**
1045: * Listener for changes of the model. Just repaints the table when
1046: * any change happened to the table model of this table.
1047: * @param changedModel
1048: */
1049: public void tableModelChanged(TableModel changedModel) {
1050: if (changedModel == model) {
1051: recomputeModelValues();
1052: invalidateTable();
1053: //repaint();
1054: }
1055: }
1056:
1057: }
|