home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / VCafe / prosrc.bin / BasicCell.java < prev    next >
Encoding:
Java Source  |  1998-03-18  |  31.7 KB  |  954 lines

  1. /*
  2.  * Copyright (c) 1997 Krumel & Associates, Inc. All Rights Reserved.
  3.  *
  4.  * www.krumel.com - controls@krumel.com
  5.  *
  6.  * Permission is given to the buyer of this package for one software
  7.  * developer to use this software on one CPU (one workstation) and to make
  8.  * one backup copy.  You may uitilize and/or modify this class for use in your
  9.  * projects.  You may distribute or sell any executable which results from
  10.  * using this code in yur application, except a utility or class of similar
  11.  * nature to this product.  You may distribute this product in compiled
  12.  * form only, but soley to be used with your cmpiled executable product
  13.  * for the puposes of dynamic loading. You may NOT redistribute the source
  14.  * code in any form or make it accessible through a network or other
  15.  * distribution media to others. Please refer to the file "copyright.html"
  16.  * for further important copyright and licensing information.
  17.  *
  18.  * The source code is the confidential and proprietary information
  19.  * of Krumel & Associates, Inc. ("Confidential Information").  You shall
  20.  * not disclose such Confidential Information and shall use it only in
  21.  * accordance with the terms of the license agreement you entered into
  22.  * with Krumel & Associates, Inc..
  23.  
  24.  * KRUMEL & ASSOCIATES MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
  25.  * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
  26.  * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  27.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. KRUMEL & ASSOCIATES SHALL NOT
  28.  * BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
  29.  * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  30.  */
  31.  
  32. package symantec.itools.db.awt;
  33.  
  34. import java.awt.*;
  35. import java.awt.event.*;
  36. import symantec.itools.db.awt.event.CellEvent;
  37.  
  38. /**
  39.  * Cell type that is used to hold string and image data.  Images are not scaled,
  40.  * this cell expects them to be small icons to the left of the text.
  41.  */
  42. public class BasicCell implements TableCell, java.awt.image.ImageObserver {
  43.     TableView           view;
  44.     DataSource          dataSource;
  45.     Coordinate          coords;
  46.     boolean             selected;
  47.     boolean             keyPressedYet;
  48.     boolean             loseFocusOnArrow;
  49.     int                 cursorPos;
  50.     int                 hlFirst, hlLast;     //left & right for highlighting
  51.     boolean             selectionMade;
  52.     int                 offset;   //will be used for keeping cursor in cell
  53.     int                 chopIndex;
  54.     int                 toLeftOfCell;
  55.     int                 startX;
  56.     boolean             defaultCell;
  57.     int                 type;
  58.  
  59.     static final int    PADSIDES = 5;
  60.  
  61.     /**
  62.      * Constructs a BasicCell.
  63.      * @param v The TableView the cell is contained within.
  64.      * @param ds The datasource the cell obtains its data.
  65.      */
  66.     public BasicCell(TableView tv, DataSource ds) {
  67.         view = tv;
  68.         dataSource = ds;
  69.     }
  70.  
  71.     /**
  72.      * Gets a copy of the cell suitable for taking the place of the current cell.
  73.      */
  74.     public TableCell cloneCell() {
  75.         BasicCell bs = new BasicCell(view, dataSource);
  76.  
  77.         if (coords != null) {
  78.             bs.coords = new Coordinate(coords.row, coords.col);
  79.         }
  80.  
  81.         bs.selected = selected;
  82.         bs.keyPressedYet = keyPressedYet;
  83.         bs.loseFocusOnArrow = loseFocusOnArrow;
  84.         bs.cursorPos = cursorPos;
  85.         bs.selectionMade = selectionMade;
  86.         bs.offset = offset;
  87.         bs.type = type;
  88.  
  89.         return bs;
  90.     }
  91.  
  92.     /**
  93.      * Gets the type of cell.
  94.      * @return One of CELL, COL_HEADING, ROW_HEADING, CORNER_CELL.
  95.      */
  96.     public int type() {
  97.         return type;
  98.     }
  99.  
  100.     /**
  101.      * Gets the auxillary control for the cell.
  102.      * @return The widget if the cell supports auxillary controls or null
  103.      *      otherwise.
  104.      */
  105.     public Component auxControl() { return null; }
  106.  
  107.     /**
  108.      * Sets the type of cell.
  109.      * @param t One of CELL, COL_HEADING, ROW_HEADING, CORNER_CELL.
  110.      * @return One of CELL, COL_HEADING, ROW_HEADING, CORNER_CELL.
  111.      */
  112.     public int type(int t) {
  113.         if (t > CORNER_CELL || t < 0) {
  114.             throw new IllegalArgumentException("Invalid cell type");
  115.         }
  116.  
  117.         return type = t;
  118.     }
  119.  
  120.  
  121.     /**
  122.      * Allows the cell to set instance variables for view and data source.
  123.      * @param v The TableView the cell is contained within.
  124.      * @param ds The datasource the cell obtains its data.
  125.      */
  126.     public void setTableView(TableView v, DataSource ds) {
  127.         view = v;
  128.         dataSource = ds;
  129.     }
  130.  
  131.     /**
  132.      * Sets whether this cell is the defualt maintained by the TableView.
  133.      * This is really an internal method that must be exposed beause of
  134.      * the rules of defining a public interface.
  135.      */
  136.     public void setDefaultFlag() {
  137.         defaultCell = true;
  138.     }
  139.  
  140.     /**
  141.      * Resets the values of the cell to their default values.  The TableView uses
  142.      * common cells to perform the drawing and event functions.  This allows it to
  143.      * use memory efficiently by not being forced to create a cell for every piece of
  144.      * data stored in a data source.  This function is called when a cell is changed
  145.      * to represent another coordinate position.  Afterwards, the cell's methods are
  146.      * called to set the internal state properly before being asked to process
  147.      * cell events are perform drawing.
  148.      */
  149.     public void reset() {
  150.         selected = false;
  151.         keyPressedYet = false;
  152.         loseFocusOnArrow = false;
  153.         cursorPos = 0;
  154.         selectionMade = false;
  155.         offset = 0;
  156.     }
  157.  
  158.     /**
  159.      * Gets whether this cell type provides support for editing.
  160.      */
  161.     public boolean isCellTypeEditable() { return true; }
  162.  
  163.     /**
  164.      * Sets the cell coordinates for the cell. The coordinate parameter is a
  165.      * shared value, so its values should be copied to another variable of type
  166.      * Coordinate.
  167.      */
  168.     public void setCoordinates(Coordinate c) {
  169.         coords = c;
  170.     }
  171.  
  172.     /**
  173.      * Gets the cell coordinates for the cell.
  174.      */
  175.     public Coordinate getCoordinates() {
  176.         return coords;
  177.     }
  178.  
  179.     /**
  180.      * Gets the row number for the cell.
  181.      */
  182.     public int row() { return coords.row; }
  183.  
  184.     /**
  185.      * Sets the row number for the cell.
  186.      */
  187.     public void setRow(int r) { coords.row = r; }
  188.  
  189.     /**
  190.      * Gets the column number for the cell.
  191.      */
  192.     public int col() { return coords.col; }
  193.  
  194.     /**
  195.      * Sets the column number for the cell.
  196.      */
  197.     public void setCol(int c) { coords.col = c; }
  198.  
  199.     /**
  200.      * Called when the user triggers a mouse event on the cell.
  201.      */
  202.     public void mouseEvent(CellEvent e) {
  203.         try {
  204.             switch(e.getID()) {
  205.                 case CellEvent.MOUSE_PRESSED:
  206.                     if (!e.getAsMouseEvent().isShiftDown()) {
  207.                         //if still highlighted position cursor right place and unhighlight
  208.                         keyPressedYet = true;
  209.                         view.setCapture();
  210.                         cursorPos = findCursorPos(e.getX());
  211.                         selectionMade = false;
  212.                     } else {
  213.                         //shift pressed so we make a hightlighted region
  214.                         hlFirst = cursorPos;
  215.                         hlLast = findCursorPos(e.getX());
  216.                         selectionMade = true;
  217.                     }
  218.  
  219.                     view.redrawCell(this);
  220.                     view.routeEvent(e);
  221.                     break;
  222.  
  223.                  case CellEvent.MOUSE_DOUBLE:
  224.                     keyPressedYet = false;
  225.                     view.redrawAsync();
  226.                     offset = 0;
  227.                     view.routeEvent(e);
  228.                     break;
  229.  
  230.                 case CellEvent.MOUSE_DRAGGED:
  231.                     if (!selectionMade && findCursorPos(e.getX())!=cursorPos) {
  232.                         //select a region
  233.                         hlFirst = cursorPos;
  234.                         selectionMade = true;
  235.                     }
  236.  
  237.                     hlLast = cursorPos = findCursorPos(e.getX());
  238.                     view.redrawCell(this);
  239.                     view.routeEvent(e);
  240.                     break;
  241.  
  242.                 case CellEvent.MOUSE_RELEASED:
  243.                     view.routeEvent(e);
  244.                     break;
  245.             }
  246.         } catch(DataNotAvailable ex) {
  247.             view.handleException(row(), col(), ex);
  248.         }
  249.     }
  250.  
  251.     /**
  252.      * Called by the TableView when it hides the auxillary control of the cell.
  253.      */
  254.     public void lostAuxControl() {}
  255.  
  256.     int findCursorPos(int x) throws DataNotAvailable {
  257.         //mouse past the beginning of string so return initial pos.
  258.         if (x < -offset) {
  259.             return 0;
  260.         }
  261.  
  262.         int total = offset + x - PADSIDES;
  263.         FontMetrics fm = view.getCellFontMetrics(this);
  264.         Data data = readData();
  265.         String text = getDataString(data);
  266.         int len = text.length();
  267.  
  268.         //loop through each letter and determine where cursor goes
  269.         Image im = data.toImage();
  270.         int align = view.getCellAlignment(this);
  271.         if (align == TableView.LEFT) {
  272.             if (offset == 0) {
  273.                 total -= PADSIDES + 2;
  274.  
  275.                 if (im != null) {
  276.                     int imageOffset = im.getWidth(this) + 2;
  277.                     int w = view.getColumnWidth(getCoordinates().col+1);
  278.                     int sw = fm.stringWidth(text);
  279.  
  280.                     if (imageOffset+sw+2+PADSIDES <= w) {
  281.                         total -= imageOffset;
  282.                     }
  283.                 }
  284.             }
  285.         } else if (align == TableView.RIGHT) {
  286.             int sw = fm.stringWidth(text);
  287.             int w = view.getColumnWidth(getCoordinates().col+1);
  288.             if (sw <= w) {
  289.                 total = total-w+sw+PADSIDES*2;
  290.             }
  291.         } else if (align == TableView.CENTER) {
  292.             int sw = fm.stringWidth(text);
  293.             int w = view.getColumnWidth(getCoordinates().col+1);
  294.             if (sw<=w) {
  295.                 total = total - (w-sw)/2;
  296.             }
  297.         }
  298.  
  299.         //check if all the way to left
  300.         if (total <= 0) { return 0; }
  301.         for(int i=1; i<=len; i++) {
  302.             if (fm.stringWidth(text.substring(0, i-1) + fm.charWidth(text.charAt(i-1))/2) > total)
  303.                 return i;
  304.         }
  305.  
  306.         return len;
  307.     }
  308.  
  309.     /**
  310.      * Gets the data object for the cell (Normally retrieved via the data source.
  311.      * @return the cell data
  312.      * @exception DataNotAvailable if the data cannot be accessed
  313.      */
  314.     public Data getData() throws DataNotAvailable {
  315.         return dataSource.getData(coords);
  316.     }
  317.  
  318.     /**
  319.      * Gets the cell data as a String.
  320.      * @return the cell data as a String
  321.      * @exception DataNotAvailable if the data cannot be accessed
  322.      */
  323.     protected String getDataString() throws DataNotAvailable {
  324.         return getDataString(readData());
  325.     }
  326.  
  327.     /**
  328.      * Gets the specified Data as a String.
  329.      * @param the data
  330.      * @param the data as a String
  331.      * @exception DataNotAvailable if the data cannot be accessed
  332.      */
  333.     protected String getDataString(Data data) throws DataNotAvailable {
  334.         if (data instanceof MappedData) {
  335.             return ((MappedData)data).displayValue();
  336.         }
  337.  
  338.         return data.toString();
  339.     }
  340.  
  341.     /**
  342.      * Reads this cell's data from the data source.
  343.      * @return this cell's data
  344.      * @exception DataNotAvailable if the data cannot be accessed
  345.      */
  346.     public Data readData() throws DataNotAvailable {
  347.         return dataSource.readData(coords.row, coords.col);
  348.     }
  349.  
  350.     void deleteChar(Data data) {
  351.         if (cursorPos == 0)
  352.             return;
  353.  
  354.         data.deleteChar(cursorPos);
  355.         cursorPos--;
  356.     }
  357.  
  358.     void deleteSelection(Data data) {
  359.         int hlLeft = Math.min(hlFirst, hlLast);
  360.         int hlRight = Math.max(hlFirst, hlLast);
  361.         for (int i=hlLeft; i<hlRight; i++) {
  362.             data.deleteChar(hlLeft+1);
  363.         }
  364.         cursorPos = hlLeft;
  365.     }
  366.  
  367.     boolean isEditable() {
  368.         try {
  369.             return isEditable(getData());
  370.         } catch (DataNotAvailable ex) {
  371.             view.handleException(row(), col(), ex);
  372.             return true;
  373.         }
  374.     }
  375.  
  376.     boolean isEditable(Data data) {
  377.         return data.isEditable(coords.row, coords.col)
  378.                && view.getCellEditable(this);
  379.     }
  380.  
  381.     /**
  382.      * Called by the TableView when the user triggers a key event while the
  383.      * cell possesses the keyboard focus.
  384.      */
  385.     public void keyEvent(CellEvent e) {
  386.         Data        data;
  387.         String      text;
  388.  
  389.         try {
  390.             data = getData();
  391.             text = getDataString(data);
  392.         } catch (DataNotAvailable ex) {
  393.             view.handleException(row(), col(), ex);
  394.             return;
  395.         }
  396.  
  397.         KeyEvent keyEvent = e.getAsKeyEvent();
  398.         boolean isActionKey = keyEvent.isActionKey();
  399.  
  400.         char c = keyEvent.getKeyChar();
  401.         boolean changed = false;
  402.         boolean handled = false;
  403.         boolean editable = isEditable(data);
  404.  
  405.         if (!editable) {
  406.             view.routeEvent(e);
  407.             return;
  408.         }
  409.  
  410.         if (isActionKey && e.getID()==CellEvent.KEY_PRESSED) {
  411.             if (keyEvent.isShiftDown() && !selectionMade) {
  412.                 selectionMade = true;
  413.                 changed = true;
  414.                 hlFirst = hlLast = cursorPos;
  415.             } else if (!keyEvent.isShiftDown() && selectionMade) {
  416.                 selectionMade = false;
  417.                 changed = true;
  418.             }
  419.  
  420.             switch(e.getKeyCode()) {
  421.                 case KeyEvent.VK_LEFT:
  422.                     if (cursorPos > 0) {
  423.                        cursorPos--;
  424.                        changed = true;
  425.                     }
  426.                     break;
  427.                 case KeyEvent.VK_RIGHT:
  428.                     if (cursorPos < text.length()) {
  429.                        cursorPos++;
  430.                        changed = true;
  431.                     }
  432.                     break;
  433.                 case KeyEvent.VK_UP:
  434.                 case KeyEvent.VK_HOME:
  435.                     if (cursorPos != 0) {
  436.                         cursorPos = 0;
  437.                         changed = true;
  438.                     }
  439.                     break;
  440.                 case KeyEvent.VK_DOWN:
  441.                 case KeyEvent.VK_END:
  442.                     int len = text.length();
  443.                     if (cursorPos != len) {
  444.                         cursorPos = len;
  445.                         changed = true;
  446.                     }
  447.                     break;
  448.             }
  449.  
  450.             if (selectionMade) hlLast = cursorPos;
  451.             if (changed) {
  452.                 view.redrawCell(this);
  453.             }
  454.  
  455.             view.routeEvent(e);
  456.             return;
  457.         }
  458.  
  459.         int keyCode = e.getKeyCode();
  460.         if (e.getID() == CellEvent.KEY_PRESSED) {
  461.             if (!keyPressedYet) {
  462.                 //if hit delete or bs then delete everything and paint cursor
  463.                 if (keyCode==KeyEvent.VK_DELETE
  464.                     || keyCode==KeyEvent.VK_BACK_SPACE)
  465.                 {
  466.                     data.clearText();
  467.                     keyPressedYet = true;
  468.                     view.setCapture();
  469.                     cursorPos = 0;
  470.                     view.redrawCell(this);
  471.                     e.setID(CellEvent.CELL_CONTENT_CHANGE);
  472.                     view.routeEvent(e);
  473.                     e.setID(CellEvent.KEY_PRESSED);
  474.                     view.routeEvent(e);
  475.                     return;
  476.                 }
  477.  
  478.                 //Strip nonprintable characters
  479.                 if (!Character.isSpace(c) && !Character.isLetterOrDigit(c)) {
  480.                     view.routeEvent(e);
  481.                     return;
  482.                 }
  483.  
  484.                 data.clearText();
  485.                 data.appendChar(c);
  486.                 keyPressedYet = true;
  487.                 selectionMade = false;
  488.                 view.setCapture();
  489.                 cursorPos = 1;      //start at beginning
  490.             } else {
  491.                 if (selectionMade) {
  492.                     deleteSelection(data);
  493.                     selectionMade = false;
  494.                     //if b.s. or del then delete highlight and return
  495.                     if (keyCode==KeyEvent.VK_BACK_SPACE ||
  496.                         keyCode==KeyEvent.VK_DELETE)
  497.                     {
  498.                         e.setID(CellEvent.CELL_CONTENT_CHANGE);
  499.                         view.routeEvent(e);
  500.                         e.setID(CellEvent.KEY_PRESSED);
  501.                         view.routeEvent(e);
  502.                         return;
  503.                     }
  504.                 }
  505.  
  506.                 switch(keyCode) {
  507.                     case KeyEvent.VK_ESCAPE:
  508.                         cursorPos = 0;
  509.                         keyPressedYet = false;
  510.                         e.setID(CellEvent.UNDO_CELL_EVENT);
  511.                         view.routeEvent(e);
  512.                         break;
  513.                     case KeyEvent.VK_ENTER:
  514.                         try {
  515.                             dataSource.commitData();
  516.                             keyPressedYet = false;
  517.                             cursorPos = 0;
  518.                         } catch(TypeNotSupported ex) {
  519.                             view.handleException(row(), col(), ex);
  520.                             return;
  521.                         }
  522.                         break;
  523.                     case KeyEvent.VK_BACK_SPACE:
  524.                         deleteChar(data);
  525.                         view.routeEvent(e);
  526.                         int align = view.getCellAlignment(this);
  527.                         if (align == TableView.RIGHT || align == TableView.CENTER) {
  528.                             offset = 0;
  529.                         }
  530.                         break;
  531.                     case KeyEvent.VK_DELETE:
  532.                         if (cursorPos != text.length()) {
  533.                             cursorPos++;
  534.                             deleteChar(data);
  535.                             e.setID(CellEvent.CELL_CONTENT_CHANGE);
  536.                             view.routeEvent(e);
  537.                         }
  538.                         break;
  539.                     default:
  540.                         //Strip nonprintable characters
  541.                         if (!isPrintableChar(c)) {
  542.                             view.routeEvent(e);
  543.                             return;
  544.                         }
  545.  
  546.                         //determine if append character or insert
  547.                         if (cursorPos==text.length()) {
  548.                             data.appendChar(c);
  549.                         } else {
  550.                             data.insertChar(cursorPos, c);
  551.                         }
  552.  
  553.                         cursorPos++;
  554.                         e.setID(CellEvent.CELL_CONTENT_CHANGE);
  555.                         view.routeEvent(e);
  556.                 }
  557.             }
  558.  
  559.             view.redrawCell(this);
  560.             e.setID(CellEvent.KEY_PRESSED);
  561.             view.routeEvent(e);
  562.             return;
  563.         } else if (e.getID() == CellEvent.KEY_RELEASED) {
  564.             view.routeEvent(e);
  565.         }
  566.     }
  567.  
  568.     boolean isPrintableChar(char c) {
  569.         switch (Character.getType(c)) {
  570.             case Character.CONTROL:
  571.             case Character.FORMAT:
  572.             case Character.LINE_SEPARATOR:
  573.             case Character.NON_SPACING_MARK:
  574.             case Character.PARAGRAPH_SEPARATOR:
  575.             case Character.PRIVATE_USE:
  576.             case Character.UNASSIGNED:
  577.             case Character.SURROGATE:
  578.                 return false;
  579.         }
  580.  
  581.         return true;
  582.     }
  583.  
  584.     /**
  585.      * Called by the TableView when the cell possesses the keyboard focus to
  586.      * query whether an arrow key can move the focus.
  587.      */
  588.     public boolean loseFocusOnArrow() {
  589.         return !selected || (selected & !keyPressedYet);
  590.     }
  591.  
  592.     /**
  593.      * Informs the cell to draw a cursor if appropriate.  The TableView deactivates
  594.      * the cursor when it loses the keyboard focus.
  595.      */
  596.     public void activateCursor() {
  597.         selected = true;
  598.         view.redrawCell(this, false);
  599.     }
  600.  
  601.     /**
  602.      * Informs the cell not to draw a cursor. The TableView deactivates the cursor
  603.      * when it loses the keyboard focus.
  604.      */
  605.     public void deactivateCursor() {
  606.         selected = false;
  607.         view.redrawCell(this, false);
  608.     }
  609.  
  610.     /**
  611.      * Called by the TableView when the cell possess the keyboard focus to query
  612.      * whether the cell's contents are in a valid state so another control may
  613.      * receive the keyboard focus.
  614.      */
  615.     public boolean canLoseFocus() {
  616.         //try to commit data - if successful then return true
  617.         try {
  618.             //commit dataSource's current data and tell the data to commit
  619.             //also
  620.             dataSource.commitData();
  621.         } catch(Exception ex) {
  622.             //Can throw RuntimeExceptions so catch everything
  623.             view.handleException(row(), col(), ex);
  624.             return false;
  625.         }
  626.  
  627.         return true;
  628.     }
  629.  
  630.     /**
  631.      * Called by the TableView when the cell either gains or loses the keyboard focus.
  632.      */
  633.     public void focusEvent(CellEvent e) {
  634.         if (e.getID() == CellEvent.GOT_FOCUS) {
  635.             selected = isEditable();
  636.             view.routeEvent(e);
  637.             view.redrawCell(this);
  638.         } else {
  639.             selected = false;
  640.             keyPressedYet = false;
  641.             selectionMade = false;
  642.             offset = 0;
  643.             cursorPos = 0;
  644.             view.routeEvent(e);
  645.             try {
  646.                 dataSource.commitData();
  647.             } catch(Exception ex) {
  648.                 view.handleException(row(), col(), ex);
  649.             }
  650.             view.redrawAroundCell(this);
  651.         }
  652.     }
  653.  
  654.     /**
  655.      * Called by the TableView when the cell is to be drawn on the screen.
  656.      * @param g The graphics context to perform drawing functions.
  657.      * @param hints Contains the information required for the cell to draw
  658.      *      in the proper place and colors.
  659.      */
  660.     public void drawCell(Graphics g, CellHints hints) {
  661.         Data        data;
  662.         String      text;
  663.  
  664.         try {
  665.             data = readData();
  666.             text = getDataString(data);
  667.         } catch (DataNotAvailable ex) {
  668.             //vj:
  669.             //view.handleException(row(), col(), ex);
  670.             data = new ImageStringData(dataSource, "");
  671.             text = "";
  672.         }
  673.  
  674.         Rectangle   r = hints.bounds();
  675.         FontMetrics fm = view.getCellFontMetrics(this);
  676.         int         asc = fm.getAscent() + 1;
  677.         int         sw = fm.stringWidth(text);
  678.         int         imageOffset = 0;
  679.         Color       oldfg = hints.fg;
  680.         int         origX = r.x,
  681.                     origWidth = r.width;
  682.  
  683.         hints.setBackground(g);
  684.         g.fillRect(r.x, r.y, r.width-1, r.height-1);
  685.         Image im = data.toImage();
  686.         switch(hints.alignment()) {
  687.             case TableView.LEFT:
  688.                 if (im != null) {
  689.                     imageOffset = im.getWidth(this) + 2;
  690.                 }
  691.  
  692.                 //check to see if it will all fit within cell
  693.                 if (imageOffset+sw+2+PADSIDES <= r.width && im != null) {
  694.                     r.x += PADSIDES;
  695.                     r.y += 1;
  696.                     g.drawImage(im, r.x, r.y, this);
  697.                     r.y -= 1;
  698.                 } else {
  699.                     imageOffset = 0;
  700.                 }
  701.  
  702.                 r.x = origX + imageOffset + PADSIDES;
  703.                 break;
  704.             case TableView.CENTER:
  705.                 if (sw>(r.width - PADSIDES*2)) {
  706.                     r.x += PADSIDES;
  707.                 } else {
  708.                     r.x = r.x + (r.width-sw)/2;
  709.                 }
  710.                 break;
  711.             case TableView.RIGHT:
  712.                 if (im != null) {
  713.                     imageOffset = im.getWidth(this);
  714.                 }
  715.                 //the 3 is just a fudge factor - there must be a better way!!!!
  716.                 //check to see if it will all fit within cell
  717.                 if ((imageOffset+2+sw+PADSIDES*2+3) <= r.width && im != null) {
  718.                     r.x = r.x + r.width - sw - imageOffset - 2 - PADSIDES - 3;
  719.                     r.y += 1;
  720.                     g.drawImage(im, r.x, r.y, this);
  721.                     r.y -= 1;
  722.                     r.x = origX + r.width - sw - PADSIDES - 3;
  723.                 } else {
  724.                     imageOffset = 0;
  725.                     if (sw>(r.width - PADSIDES*2)) {
  726.                         r.x += PADSIDES;
  727.                     } else {
  728.                         r.x = origX + r.width - sw - PADSIDES - 3;
  729.                     }
  730.                 }
  731.                 break;
  732.         }
  733.  
  734.         hints.setForeground(g);
  735.         r.width = r.width + origX - r.x - PADSIDES;
  736.         startX = r.x;
  737.         drawText(data, g, r, fm, hints);
  738.         r.width = origWidth;
  739.  
  740.         //draw cursor
  741.         if (selected && keyPressedYet) {
  742.             int cpos = 0;
  743.             if (text.length() > 0) {
  744.                 cpos = fm.stringWidth(data.subString(toLeftOfCell, cursorPos));
  745.             }
  746.             g.drawLine(r.x + cpos,
  747.                        r.y + 2,
  748.                        r.x + cpos,
  749.                        r.y + fm.getHeight());
  750.         }
  751.  
  752.  
  753.         r.x = origX;
  754.  
  755.         hints.drawBoundary(g);
  756.     }
  757.  
  758.     /**
  759.      * Gets a string from the data source that is truncated as needed to
  760.      * fit within the given rectangle.
  761.      * @param data the data source
  762.      * @param r the clipping rectangle
  763.      * @param fm the text font metrics
  764.      * @return the string, truncated as needed
  765.      */
  766.     protected String chopString(Data data, Rectangle r, FontMetrics fm) {
  767.         String text;
  768.  
  769.         try {
  770.             text = getDataString(data);
  771.         } catch(DataNotAvailable ex) { return ""; }
  772.  
  773.         int w = view.getColumnWidth(coords.col+1);
  774.         chopIndex = 1;
  775.         String t;
  776.  
  777.         if (text.length() == 0) {
  778.             return text;
  779.         }
  780.  
  781.         //chop off any part of string not visible
  782.         setOffset(text, r, fm);
  783.         if (offset != 0) {
  784.             int left;
  785.             while(toLeftOfCell < text.length()) {
  786.                 left = fm.stringWidth(text.substring(0, toLeftOfCell));
  787.                 if (left < offset + PADSIDES) {
  788.                     toLeftOfCell++;
  789.                 } else {
  790.                     toLeftOfCell--;
  791.                     break;  //found the visible portion of the text
  792.                 }
  793.             }
  794.  
  795.             toLeftOfCell -= (toLeftOfCell < text.length()) ?0  :1;
  796.             text = text.substring(toLeftOfCell, text.length());
  797.         } else {
  798.             toLeftOfCell = 0;
  799.         }
  800.  
  801.         //get the portion of string that fits into cell
  802.         do {
  803.             t = text.substring(0, chopIndex);
  804.         } while(fm.stringWidth(t) < w - PADSIDES*2 && chopIndex++ < text.length());
  805.  
  806.         return text.substring(0, --chopIndex);
  807.     }
  808.  
  809.     /**
  810.      * Draws the given cell data on the screen.
  811.      * @param data the cell data
  812.      * @param g the graphics context to perform drawing functions
  813.      * @param r the cell's clipping rectangle
  814.      * @param fm the metrics of the drawing font
  815.      * @param hints information required for the cell to draw
  816.      *      in the proper place and colors.
  817.      */
  818.     protected void drawText(Data data, Graphics g, Rectangle r,
  819.                             FontMetrics fm, CellHints hints)
  820.     {
  821.         String text = chopString(data, r, fm);
  822.  
  823.         if (view.printMode()) {
  824.             g.drawString(text, r.x, r.y+fm.getAscent()+2);
  825.             return;
  826.         }
  827.  
  828.         if (selected && !keyPressedYet) {
  829.             //all highlighted and ready for delete
  830.             hints.setForeground(g);
  831.             g.fillRect(r.x-1,
  832.                        r.y+3,
  833.                        fm.stringWidth(text.toString())+4,
  834.                        fm.getHeight()-1);
  835.             hints.setBackground(g);
  836.             g.drawString(text, r.x, r.y+fm.getAscent()+2);
  837.         } else if (selected && !selectionMade) {
  838.             //draw string with cursor
  839.             g.drawString(text, r.x, r.y+fm.getAscent()+2);
  840.         } else if(!selected || !selectionMade ) {
  841.             //no highlight
  842.             g.drawString(text, r.x, r.y+fm.getAscent()+2);
  843.         } else {
  844.             //some section must be highlighted
  845.             int hlLeft = Math.min(hlFirst, hlLast);
  846.             int hlRight = Math.max(hlFirst, hlLast);
  847.             hlRight = Math.min(hlRight, toLeftOfCell+chopIndex);
  848.  
  849.             if (toLeftOfCell != 0) {
  850.                 hlLeft = Math.max(hlLeft - toLeftOfCell, 0);
  851.                 hlRight = hlRight - toLeftOfCell;
  852.             }
  853.  
  854.             String t = text.substring(0, hlLeft);
  855.  
  856.             //draw first unselected portion
  857.             g.drawString(t, r.x, r.y+fm.getAscent()+2);
  858.  
  859.             //draw middle selected portion
  860.             int pixelHLStart = fm.stringWidth(t) + r.x;
  861.             t = text.substring(hlLeft, hlRight);
  862.             int pixelHLStop = fm.stringWidth(t) + pixelHLStart;
  863.             hints.setForeground(g);
  864.             g.fillRect(pixelHLStart,
  865.                        r.y+2,
  866.                        pixelHLStop - pixelHLStart,
  867.                        fm.getHeight()-1);
  868.             hints.setBackground(g);
  869.             g.drawString(t, pixelHLStart, r.y+fm.getAscent()+2);
  870.  
  871.             //draw end unselected portion
  872.             hints.setForeground(g);
  873.             t = text.substring(hlRight);
  874.             g.drawString(t, pixelHLStop, r.y+fm.getAscent()+2);
  875.         }
  876.     }
  877.  
  878.     /**
  879.      * Sets the offset from the start of the string to left side of spcified rectangle.
  880.      */
  881.     void setOffset(String text, Rectangle r, FontMetrics fm) {
  882.         //vj: Reset cursor position to the end of the text. Required because of forced
  883.         //    commit of the current cell's data
  884.         if (cursorPos > text.length())
  885.         cursorPos = text.length();
  886.         String sub = text.substring(0, cursorPos);
  887.         int toCursor = fm.stringWidth(sub);
  888.         int fudge = 3;
  889.  
  890.         if (!selected) {
  891.             offset = 0;
  892.             return;
  893.         }
  894.  
  895.         //if cursor pos is not currently visible, put the cursor in sight within
  896.         //1/3 width of rectangle
  897.         if (toCursor > r.width - fudge + offset) {
  898.             //cursor to far to right
  899.             offset = -r.width + toCursor + fudge + r.width/3;
  900.             //don't left right side of string off the right side of cell
  901.             offset = Math.min(offset, fm.stringWidth(text) - r.width + fudge);
  902.         } else if (toCursor < offset) {
  903.             //cursor too far to left
  904.             offset = Math.max(toCursor + fudge - r.width/3, 0);
  905.  
  906.             //amount of string to left of cell just decreased so force recompute
  907.             toLeftOfCell = 0;
  908.         }
  909.     }
  910.  
  911.     /**
  912.      * Repaints the list when the cell's image has changed.
  913.      * @return true if image has changed; false otherwise.
  914.      */
  915.     public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
  916.         if ((flags & (ABORT|ERROR)) != 0) {
  917.             return false;
  918.         }
  919.  
  920.         if ((flags & ALLBITS) != 0) {
  921.             view.redrawAsync();
  922.             return false;
  923.         } else {
  924.             return true;
  925.         }
  926.     }
  927.  
  928.     /**
  929.      * Inherited from java.lang.Object.
  930.      * @return The string data for the cell.
  931.      */
  932.     public String toString() {
  933.         try {
  934.             return readData().toString();
  935.         } catch(DataNotAvailable ex) {
  936.             return "ERROR getting data";
  937.         }
  938.     }
  939.  
  940.     /**
  941.      * Gets a string with the coordinates and the string data for cell.
  942.      */
  943.     public String stats() {
  944.         try {
  945.             return "BasicCell: row=" + coords.row + " col=" + coords.col +
  946.                 " text=" + readData().toString();
  947.         } catch(DataNotAvailable ex) {
  948.             return "BasicCell: row=" + coords.row + " col=" + coords.col +
  949.                    " could not retrieve data";
  950.         }
  951.     }
  952. }
  953.  
  954.