    001package org.vishia.gral.swt;
    002
    003
    004
    005
    006import org.eclipse.swt.SWT;
    007import org.eclipse.swt.events.ControlEvent;
    008import org.eclipse.swt.events.ControlListener;
    009import org.eclipse.swt.events.FocusEvent;
    010import org.eclipse.swt.events.FocusListener;
    011import org.eclipse.swt.events.KeyEvent;
    012import org.eclipse.swt.events.KeyListener;
    013import org.eclipse.swt.events.MouseEvent;
    014import org.eclipse.swt.events.MouseListener;
    015import org.eclipse.swt.events.MouseWheelListener;
    016import org.eclipse.swt.events.PaintEvent;
    017import org.eclipse.swt.events.PaintListener;
    018import org.eclipse.swt.events.SelectionEvent;
    019import org.eclipse.swt.events.SelectionListener;
    020import org.eclipse.swt.events.TraverseEvent;
    021import org.eclipse.swt.events.TraverseListener;
    022import org.eclipse.swt.graphics.Color;
    023import org.eclipse.swt.graphics.Font;
    024import org.eclipse.swt.graphics.GC;
    025import org.eclipse.swt.graphics.Point;
    026import org.eclipse.swt.graphics.Rectangle;
    027import org.eclipse.swt.widgets.Canvas;
    028import org.eclipse.swt.widgets.Composite;
    029import org.eclipse.swt.widgets.Control;
    030import org.eclipse.swt.widgets.Menu;
    031import org.eclipse.swt.widgets.Text;
    032import org.eclipse.swt.widgets.Widget;
    033import org.vishia.gral.base.GralMenu;
    034import org.vishia.gral.base.GralTable;
    035import org.vishia.gral.base.GralWidgImpl_ifc;
    036import org.vishia.gral.base.GralWidget;
    037import org.vishia.gral.base.GralMng;
    038import org.vishia.gral.ifc.GralColor;
    039import org.vishia.gral.ifc.GralRectangle;
    040import org.vishia.gral.ifc.GralTableLine_ifc;
    041import org.vishia.gral.ifc.GralUserAction;
    042import org.vishia.gral.ifc.GralWidget_ifc;
    043import org.vishia.util.Assert;
    044import org.vishia.util.Debugutil;
    045import org.vishia.util.KeyCode;
    046
    047/**Implementation of the GralTable for Swt graphic.
    048 * The following schema shows the aggregation relations, see {@link org.vishia.util.Docu_UML_simpleNotation}:
    049 * <pre>
    050 *                      |--widgg----------------->GralWidget<|-------+
    051 *                      |                             |              |
    052 *            +--|>GralWidget.ImplAccess<--widgImpl---|              |
    053 *            |                                                      |
    054 *            |                                                      |
    055 *    +-|>GralTable.GraphicImplAccess                                |
    056 *    |                   |                                          |
    057 * SwtTable               |                                     GralTable
    058 *    |                   |                                          |
    059 *    |                   |<-------------------------------------gi--|
    060 *    |                   |                                          |
    061 *    |                   |<&>-------------------------------------&>|
    062 *    |                        access to all protected methods and fields of GralTable
    063 *    |
    064 *    |
    065 *    |
    066 *    |---swtWidg----->SwtWidgetHelper
    067 *    |                       |
    068 *    |                       |
    069 *    |                       |--widgetSwt--->Control<|---Composite<|---SwtTable.Table
    070 *    |                                                      |               |
    071 *    |---cellsSwt--------------*>Text<*---------------------|            some Listener
    072 *    |
    073 *    |
    074 * </pre>
    075 * <ul>
    076 * <li>The {@link GralTable} contains some implementation-non-specific things, most of table structure.
    077 * <li>To access from this class to the GralTable, the inner class {@link GralTable.GraphicImplAccess}
    078 *   is used as super class of SwtTable with protected access.
    079 * <li>The GralTable knows this class via the interface {@link GralWidgImpl_ifc} in its superclass
    080 *   association {@link GralWidget#_wdgImpl}.
    081 * <li>The GralTable knows this class via {@link GralTable#gi} association with the proper type.
    082 * <li>The {@link GralTable.GraphicImplAccess} defines some abstract methods which are implemented here.
    083 * <li>But some implementations of {@link GralWidgImpl_ifc} is found in {@link SwtWidgetHelper}.
    084 *   That are unique implementations, reuse it!
    085 * <li>Therefore for reused implementations this class delegates the interface to {@link #swtWidgHelper}.
    086 * <li>The Swt widget core implementation is {@link Table} which is derived from
    087 *   {@link org.eclipse.swt.widgets.Composite}. That Composite contains some text fields
    088 *   which are the visible rows and columns of the table.
    089 * <li>The text fields are created as children of the swt.widgets.Composite but associated
    090 *   in this class: {@link #cellsSwt}. Therefore this class can access the visible rows and columns
    091 *   directly, using as {@link org.eclipse.swt.widgets.Text}.
    092 * <li>The inner class {@link Table} is necessary only as implementor of {@link GralWidgetGthreadSet_ifc}.
    093 *   But that is an {@link Deprecated} interface. It is possible to replace it later with an simple
    094 *   instance of Composite.
    095 * <li>The content of the table itself is stored in an array of {@link GralTable#tableLines},
    096 *   see description of {@link GralTable}.
    097 * </ul>
    098 * @author Hartmut Schorrig
    099 *
    100 */
    101public class SwtTable  extends GralTable<?>.GraphicImplAccess implements GralWidgImpl_ifc
    102, FocusListener  //for the cells
    103//public class SwtTable  implements GralWidgImpl_ifc
    104{
    105
    106  /**Version and history
    107   * <ul>
    108   * <li>2018-01-07 Hartmut new: {@link #getCellTextFocus()}
    109   * <li>2015-08-29 Hartmut chg: It has a {@link #traverseListenerTable} now to accept 'tab' and 'sh-tab' as normal key
    110   *   instead usage to traverse between the text fields of a table. TODO: What traversing functions are missing yet?
    111   *   They should not any traverse function between the cells.
    112   * <li>2013-06-29 Hartmut chg: refactoring. Now a GralTable<generic> can be created before the graphic is build. It is the new schema of GralWidget.
    113   *   The inner class {@link GraphicImplAccess} is provided as super class for the graphic implementation class,
    114   *   for example {@link org.vishia.gral.swt.SwtTable}.
    115   * <li>2013-10-08 Hartmut chg:
    116   * <li>2012-07-15 Hartmut new: search functionality: This implementation have a text field
    117   *   which shows the search string. While up and down keys the that lines are selected which text in the {@link #ixColumn()}
    118   *   starts whith the search string.
    119   * <li>2012-01-06 Hartmut new: concept of a table which is independent of the table implementation
    120   *   of the table implementations in the graphic system layer or in operation system:
    121   *   The capability  of a SWT-table is not sufficient, for example the color of the selection bar
    122   *   is not able to change. Other reason: Implementation of table in SWT, AWT, Swing is different.
    123   *   It seems better to have one table concept with independent features, which based on simple widgets.
    124   *   is not
    125   * </ul>
    126   *
    127   * <b>Copyright/Copyleft</b>:
    128   * For this source the LGPL Lesser General Public License,
    129   * published by the Free Software Foundation is valid.
    130   * It means:
    131   * <ol>
    132   * <li> You can use this source without any restriction for any desired purpose.
    133   * <li> You can redistribute copies of this source to everybody.
    134   * <li> Every user of this source, also the user of redistribute copies
    135   *    with or without payment, must accept this license for further using.
    136   * <li> But the LPGL ist not appropriate for a whole software product,
    137   *    if this source is only a part of them. It means, the user
    138   *    must publish this part of source,
    139   *    but don't need to publish the whole source of the own product.
    140   * <li> You can study and modify (improve) this source
    141   *    for own using or for redistribution, but you have to license the
    142   *    modified sources likewise under this LGPL Lesser General Public License.
    143   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    144   * </ol>
    145   * If you are intent to use this sources without publishing its usage, you can get
    146   * a second license subscribing a special contract with the author.
    147   *
    148   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    149   *
    150   */
    151  public final static String version = "2015-08-29";
    152
    153  /**It contains the association to the swt widget (Control) and the {@link SwtMng}
    154   * and implements some methods of {@link GralWidgImpl_ifc} which are delegate from this.
    155   */
    156  private final SwtWidgetHelper swtWidgHelper;
    157
    158  /**Some SWT Text Control fields. There are the visible cells of the table. They are children
    159   * of the Composite in {@link SwtWidgetHelper#widgetSwt}. */
    160  private final Text[][] cellsSwt;
    161
    162  private Canvas vScrollBar;
    163
    164  private Text cellInFocus;
    165
    166  Color colorBackVscrollbar, colorSliderVscrollbar;
    167
    168  /**SWT Text Control which contains a search text. It is only visible if need. */
    169  protected Text swtSearchText;
    170
    171  //private final SwtTable.Table table;
    172
    173  private final FocusListener focusListenerTable;
    174
    175  //private final FocusListener focusListenerCell;
    176
    177  private final MousePressedListenerTable mousePressedListener = new MousePressedListenerTable();
    178
    179  private final MouseWheelListenerTable mouseWheelListener = new MouseWheelListenerTable();
    180
    181  private final TableKeyListerner myKeyListener;
    182
    183  public SwtTable(GralTable<?> gralTable, SwtMng mng, Composite parent)
    184  { gralTable.super(gralTable, mng.mng);
    185    //super(name, mng, columnWidths);
    186    this.myKeyListener = this.new TableKeyListerner(null);
    187    focusListenerTable = this.new FocusListenerTable(mng);
    188    //focusListenerCell = this;
    189    setColorsSwt();
    190    int zLineVisibleMax = gralTable.nrofLinesVisibleMax();
    191    this.cellsSwt = new Text[zLineVisibleMax][zColumn()];
    192    int zColumn = zColumn();
    193    Composite swtTable = new SwtTable.Table(parent, zColumn, mng);
    194    initSwtTable(swtTable, zColumn, mng);
    195    vScrollBar = new Vscrollbar(swtTable);
    196    super.wdgimpl = this.swtWidgHelper = new SwtWidgetHelper(swtTable, mng);
    197    //gralTable.implMethodWidget_.setWidgetImpl(this);
    198    //this.menuColumns = new SwtMenu[zColumn];
    199    swtWidgHelper.widgetSwt.addKeyListener(myKeyListener);
    200    //table.addSelectionListener(selectionListener);
    201    swtWidgHelper.widgetSwt.addControlListener(resizeListener);
    202    swtWidgHelper.widgetSwt.addFocusListener(focusListenerTable);
    203
    204    swtWidgHelper.widgetSwt.setFont(mng.propertiesGuiSwt.stdInputFont);
    205    GralRectangle pixTable = swtWidgHelper.mng.setBounds_(widgg.pos(), swtWidgHelper.widgetSwt);
    206    SwtTable.this.resizeTable(pixTable);
    207  }
    208
    209
    210
    211
    212  /**
    213   * @param gralTable
    214   * @param mng
    215   * @param sName
    216   * @param height
    217   * @param columnWidths
    218   * @return
    219   * @deprecated Create an instance of {@link GralTable} and call
    220   */
    221  @Deprecated
    222  public static GralTable addTable(GralTable gralTable, SwtMng mng, String sName, int height, int[] columnWidths
    223      //, int selectionColumn, CharSequence selectionText
    224      ) {
    225
    226        boolean TEST = false;
    227        final SwtTable table;
    228        Composite parent = mng.getCurrentPanel();
    229        table = new SwtTable(gralTable, mng, parent); //, selectionColumn, selectionText);
    230        table.outer.setDataPath(sName);
    231        table.swtWidgHelper.widgetSwt.setData(table);
    232        mng.mng.registerWidget(gralTable);
    233        //NOTE done in SwtTable.resize()     ((SwtMng)mng).setPosAndSize_(table.table);
    234        return gralTable;
    235
    236      }
    237
    238
    239  /*package private*/
    240  static void createTable(GralTable<?> gralTable, SwtMng mng) {
    241
    242    Composite parent = mng.getWidgetsPanel(gralTable);
    243    @SuppressWarnings("unchecked")
    244    final SwtTable table = new SwtTable(gralTable, mng, parent); //, selectionColumn, selectionText);
    245    table.swtWidgHelper.widgetSwt.setData(table);
    246    mng.mng.registerWidget(gralTable);
    247    //NOTE done in SwtTable.resize()     ((SwtMng)mng).setPosAndSize_(table.table);
    248
    249  }
    250
    251
    252  private void setColorsSwt(){
    253    //colorBackSelectSwt = mng.getColorImpl(colorBackSelect);
    254    //colorBackMarkedSwt = mng.getColorImpl(colorBackMarked);
    255    //colorBackTableSwt = mng.getColorImpl(colorBackTable);
    256    //colorBackSelectNonFocusedSwt = mng.getColorImpl(colorBackSelectNonFocused);
    257    //colorBackMarkedNonFocusedSwt = mng.getColorImpl(colorBackMarkedNonFocused);
    258    //colorBackTableNonFocusedSwt = mng.getColorImpl(colorBackTableNonFocused);
    259  }
    260
    261
    262
    263
    264  @Override public Object getWidgetImplementation() {
    265    return swtWidgHelper.widgetSwt;
    266  }
    267
    268  //@Override
    269  public GralColor setBackgroundColor(GralColor color) {
    270    // TODO Auto-generated method stub
    271    return null;
    272  }
    273
    274  //@Override
    275  public GralColor setForegroundColor(GralColor color) {
    276    // TODO Auto-generated method stub
    277    return null;
    278  }
    279
    280  @Override
    281  public void setBoundsPixel(int x, int y, int dx, int dy) {
    282    // TODO Auto-generated method stub
    283
    284  }
    285
    286
    287
    288  @Override public GralRectangle getPixelPositionSize(){ return swtWidgHelper.getPixelPositionSize(); }
    289
    290
    291  //@Override
    292  public boolean XXXsetVisible(boolean visible){
    293    boolean ret = swtWidgHelper.widgetSwt.isVisible();
    294    swtWidgHelper.widgetSwt.setVisible(visible);
    295    ((GralWidget.ImplAccess)this).setVisibleState(visible);
    296    return ret;
    297  }
    298
    299
    300
    301  /** TODO implement in {@link GralTable.GraphicImplAccess}
    302   * @see org.vishia.gral.base.GralWidgImpl_ifc#repaintGthread()
    303   */
    304  @Override public void repaintGthread(){
    305    if(bFocusLost){
    306      //this is about 50 ms after focus lost, the focus has lost really.
    307      bFocused = false;
    308      bFocusLost = false;
    309      setFocused(widgg, false);
    310    }
    311    if(swtWidgHelper.widgetSwt !=null && !swtWidgHelper.widgetSwt.isDisposed()){
    312      int chg = getChanged();
    313      int acknChg = 0;
    314      if((chg & chgVisible)!=0){
    315        acknChg |= chgVisible;
    316        setVisibleState(true);
    317        swtWidgHelper.widgetSwt.setVisible(true);  //the composite.
    318        //for(Text[] lines: cellsSwt){
    319        //  for(Text cell: lines){ cell.setVisible(true); }
    320        //}
    321      }
    322      if((chg & chgInvisible)!=0){
    323        acknChg |= chgInvisible;
    324        setVisibleState(false);
    325        swtWidgHelper.widgetSwt.setVisible(false);  //the composite.
    326        //for(Text[] lines: cellsSwt){
    327        //  for(Text cell: lines){ cell.setVisible(false); }
    328        //}
    329      }
    330      acknChanged(acknChg);
    331      if(widgg.isVisible()){
    332        //
    333        updateGraphicCellContent();  //invokes drawCellContent(...) with the correct lines.
    334        //^^^^^^^^^^^^^^^^^
    335        //setAllCellContentGthread();
    336        Color colorSelectBack =  swtWidgHelper.mng.getColorImpl(super.colorSelectCharsBack());
    337        Color colorSelect =  swtWidgHelper.mng.getColorImpl(super.colorSelectChars());
    338        if(super.searchChars().length() >0){
    339          swtSearchText.setBackground(colorSelectBack);
    340          swtSearchText.setForeground(colorSelect);
    341          //swtSearchText.
    342          swtSearchText.setText(searchChars().toString());
    343          Point tabSize = swtWidgHelper.widgetSwt.getSize();
    344          swtSearchText.setBounds(xpixelCell[ixColumn()] + 10, tabSize.y - linePixel, xpixelCell[ixColumn()+1] - xpixelCell[ixColumn()] - 10, linePixel);
    345          swtSearchText.setVisible(true);
    346
    347        } else {
    348          swtSearchText.setVisible(false);
    349        }
    350        //System.out.println("swtTable redrawed");
    351        //see redrawChildren. This redraw don't influences the vScrollbar:
    352        //swtWidgHelper.widgetSwt.redraw();
    353        //swtWidgHelper.widgetSwt.update();  //this is the core-redraw
    354        //redraw command for the:
    355        if(bVscrollbarChanged){
    356          vScrollBar.update();
    357          vScrollBar.redraw();
    358        }
    359      }
    360      //((Table)swtWidg.widgetSwt).super.redraw();
    361      redrawtime = System.currentTimeMillis();
    362      super.bChangedLinesForCell(false);
    363      //System.out.println("test SwtTable redraw " + ++redrawct);
    364    } else {
    365      System.out.println("test SwtTable redraw disposed" + ++redrawct);
    366    }
    367    bRedrawPending = false;
    368
    369  }
    370
    371
    372  /**Sets the focus to the current cell of the tab
    373   * @see org.vishia.gral.base.GralWidget#setFocusGThread()
    374   * TODO this method must call in the graphic thread yet, queue it with {@link GralMng#setInfo(GralWidget, int, int, Object, Object)}.
    375   */
    376  @Override public boolean setFocusGThread()
    377  { repaintGthread();  //to set the focus of the cell
    378    return true;
    379  }
    380
    381
    382  @Override public void setVisibleGThread(boolean bVisible) {
    383    swtWidgHelper.setVisibleGThread(bVisible);
    384    super.setVisibleState(bVisible);
    385    for(Text[] cell1 : cellsSwt) for(Text cell: cell1){
    386      if(cell !=null) {
    387        cell.setVisible(bVisible);
    388      }
    389    }
    390    repaintGthread();
    391  }
    392
    393
    394  @Override
    395  protected void setDragEnable(int dragType)
    396  { throw new IllegalArgumentException("drag not supported for this widget type");
    397  }
    398
    399
    400  /**Removes the graphical widgets.
    401   * @see org.vishia.gral.base.GralWidget#removeWidgetImplementation()
    402   */
    403  @Override public void removeWidgetImplementation() {
    404    // TODO Auto-generated method stub
    405    if(!swtWidgHelper.widgetSwt.isDisposed()){
    406      for(int iRow = 0; iRow < cellsSwt.length; ++iRow){
    407        for(int iCol = 0; iCol < zColumn(); ++iCol){
    408          Text cell = cellsSwt[iRow][iCol];
    409          cell.dispose();
    410          cellsSwt[iRow][iCol] = null;
    411        }
    412      }
    413      swtWidgHelper.widgetSwt.dispose();  //it may be sufficient to dispose table only becaust it is the container....
    414    }
    415  }
    416
    417  @Override public boolean remove(){
    418    super.remove(); //removes the widget implementation.
    419    return true;
    420  }
    421
    422
    423  /**Sets the current cell as focused with the focus color. It causes
    424   * a redraw of the whole table because the cell may be shifted in table position.
    425   * TODO this method must call in the graphic thread yet, queue it with {@link GralMng#setInfo(GralWidget, int, int, Object, Object)}.
    426   * @param cell The cell
    427   */
    428  private void redrawTableWithFocusedCell(Widget cell){
    429    GralTable.CellData data = (GralTable.CellData)cell.getData();
    430    data.bSetFocus = true;
    431    if(super.redrawTableWithFocusedCell(data)){
    432      repaintGthread();
    433      //((Table)swtWidgWrapper.widgetSwt).redrawGthread();
    434    }
    435  }
    436
    437
    438
    439  @Override protected int getVisibleLinesTableImpl(){
    440    Rectangle size = swtWidgHelper.widgetSwt.getBounds();
    441    return size.height / linePixel;
    442  }
    443
    444
    445  /**This routine implements all things to set the content of any table cell to show it.
    446   * @see org.vishia.gral.base.GralTable#drawCellContent(int, int, org.vishia.gral.base.GralTable.TableLineData)
    447   */
    448  @Override protected void drawCellContent(int iCellLine, GralTable.CellData[] cellLine, GralTable<?>.TableLineData line, GralTable.LinePresentation linePresentationP){
    449    Text[] textlineSwt = cellsSwt[iCellLine];
    450    int treeDepth = -1;
    451    if(line !=null && line.treeDepth() != cellLine[0].treeDepth){
    452      treeDepth =  cellLine[0].treeDepth = line.treeDepth();
    453    }
    454    if(line !=null){
    455      for(int col=0; col < cells[0].length; ++col){
    456        Text cellSwt = textlineSwt[col];
    457        GralTable.CellData cellData = cellLine[col]; //(GralTable.CellData)cellSwt.getData();
    458        if(treeDepth >=0 && col < super.nrofColumnTreeShift){
    459          int xleft = xpixelCell[col] + treeDepth * xPixelUnit;
    460          int xRight = xpixelCell[col+1] + (col < (super.nrofColumnTreeShift-1) ?  treeDepth * xPixelUnit : 0);
    461          int yTop = cellData.ixCellLine * linePixel;
    462          cellSwt.setBounds(xleft, yTop, xRight - xleft, linePixel);
    463        }
    464        //
    465        if(line.wasCelltextSet(col) || bChangedLinesForCell()) {
    466          String text = line.cellTexts[col];
    467          if(text == null){ text = ""; }
    468          //
    469          cellSwt.setText(text);
    470        }
    471        if(cellData.bSetFocus){
    472          cellSwt.setFocus();
    473          cellData.bSetFocus = false;
    474        }
    475        GralColor colorBackCell = linePresentationP.cellsColorBack !=null && linePresentationP.cellsColorBack[col] !=null
    476            ? linePresentationP.cellsColorBack[col] : linePresentationP.colorBack;
    477        if(cellData.colorBack != colorBackCell){
    478          //only change color of the SWT Text field if it is necessary, comparison set color with cellData.color.
    479          Color colorSwt =  swtWidgHelper.mng.getColorImpl(colorBackCell);
    480          cellSwt.setBackground(colorSwt);
    481          cellData.colorBack = colorBackCell;  //for the visible cell swt widget, not for the table line!
    482        }
    483        if(cellData.colorText != linePresentationP.colorText){
    484          //only change color of the SWT Text field if it is necessary, comparison set color with cellData.color.
    485          cellSwt.setForeground(swtWidgHelper.mng.getColorImpl(linePresentationP.colorText));
    486          cellData.colorText = linePresentationP.colorText;
    487        }
    488        cellSwt.setVisible(true);
    489        cellSwt.redraw();
    490      }
    491    } else {
    492      //empty line:
    493      GralColor colorBack = colorBackTable();
    494      for(int col=0; col < cells[0].length; ++col){
    495        Text cellSwt = textlineSwt[col];
    496        GralTable.CellData cellData = cellLine[col]; //(GralTable.CellData)cellSwt.getData();
    497        cellSwt.setText("");
    498        if(cellData.colorBack != colorBack){
    499          Color colorSwt =  swtWidgHelper.mng.getColorImpl(colorBack);
    500          cellSwt.setBackground(colorSwt);
    501          cellData.colorBack = colorBack;  //for the visible cell swt widget, not for the table line!
    502        }
    503      }
    504    }
    505  }
    506
    507
    508
    509
    510
    511  @Override protected GralTable.CellData drawCellInvisible(int iCellLine, int iCellCol){
    512    Text cellSwt = cellsSwt[iCellLine][iCellCol];
    513    GralTable.CellData cellData = (GralTable.CellData)cellSwt.getData();
    514    cellSwt.setText("");
    515    if(cellData.colorBack != colorBackTable()){
    516      Color colorSwt =  swtWidgHelper.mng.getColorImpl(colorBackTable());
    517      cellSwt.setBackground(colorSwt);
    518      cellData.colorBack = colorBackTable();
    519    }
    520    cellSwt.setVisible(true);
    521    return (cellData);
    522  }
    523
    524
    525
    526
    527  private void paintVscrollbar(GC gc, Canvas canvas)
    528  {
    529
    530    if(ixColorScrollbar != ixColorScrollbarLast) {
    531      //only get a new color if necessary.
    532      colorBackVscrollbar = swtWidgHelper.mng.getColorImpl(colorBackVscrollbar());
    533      colorSliderVscrollbar = swtWidgHelper.mng.getColorImpl(colorLineVscrollbar());
    534      ixColorScrollbarLast = ixColorScrollbar;
    535    }
    536    Rectangle dim = canvas.getBounds();
    537    determineSizeAndPositionScrollbar(dim.height);
    538    gc.setForeground(colorBackVscrollbar);
    539    gc.fillRectangle(1, dim.y, dim.width, dim.height);
    540    gc.setForeground(colorSliderVscrollbar);
    541    gc.setBackground(colorSliderVscrollbar);
    542    //Note: relative coordinates inside the canvas area:
    543    gc.fillRectangle(1, y1Scrollbar, dim.width-1, y2Scrollbar - y1Scrollbar);
    544    //gc.drawLine(1, y1Scrollbar+1, dim.width-1, y2Scrollbar); // - y1Scrollbar-1);
    545
    546    //gc.drawLine(1,1, dim.width-1, dim.height-1);
    547
    548  }
    549
    550
    551
    552
    553  @Override protected GralMenu createColumnMenu(int column){
    554    //GralMenu menuColumn = new SwtMenu(outer, swtWidgWrapper.widgetSwt, itsMng());
    555    GralMenu menuColumn = new GralMenu();
    556    new SwtMenu(menuColumn, null, cellsSwt[0][column]);
    557    for(int iRow = 1; iRow < cellsSwt.length; ++iRow){
    558      //uses the same menu instance in all cells of the column.
    559      cellsSwt[iRow][column].setMenu((Menu)menuColumn.getMenuImpl());
    560    }
    561    return menuColumn;
    562  }
    563
    564
    565  /**Called internal from mouse event only.
    566   * @param ev
    567   */
    568  protected void mouseDown(MouseEvent ev){
    569    Text widgSwt = (Text)ev.widget;  //it is only associated to a cell.
    570    GralTable.CellData cell = (GralTable.CellData)widgSwt.getData();
    571    int key = SwtGralKey.convertMouseKey(ev.button, SwtGralKey.MouseAction.down, ev.stateMask);
    572    super.mouseDownGral(key, cell);  //To independent GRAL layer
    573  }
    574
    575
    576
    577  /**Called internal from mouse event only.
    578   * @param ev
    579   */
    580  protected void mouseUp(MouseEvent ev){
    581    Text widgSwt = (Text)ev.widget;  //it is only associated to a cell.
    582    GralTable.CellData cell = (GralTable.CellData)widgSwt.getData();
    583    int key = SwtGralKey.convertMouseKey(ev.button, SwtGralKey.MouseAction.up, ev.stateMask);
    584    super.mouseUpGral(key, cell);    //To independent GRAL layer
    585  }
    586
    587
    588
    589  /**Called internal from mouse event only.
    590   * @param ev
    591   */
    592  protected void mouseDouble(MouseEvent ev){
    593    Text widgSwt = (Text)ev.widget;  //it is only associated to a cell.
    594    GralTable.CellData cell = (GralTable.CellData)widgSwt.getData();
    595    int key = SwtGralKey.convertMouseKey(ev.button, SwtGralKey.MouseAction.down, ev.stateMask);
    596    super.mouseDoubleGral(key, cell);    //To independent GRAL layer
    597  }
    598
    599
    600
    601  @Override protected void setBoundsCells(int treeDepthBase, int zLineVisible){
    602    int yPix = 0;
    603    //int xPixelUnit = swtWidgHelper.mng.mng.propertiesGui.xPixelUnit();
    604    int ixrow = 0;
    605    for(Text[] row: cellsSwt){
    606      if(ixrow < zLineVisible) {
    607        int ixColumn = 0;
    608        for(Text cell: row){
    609          cell.setVisible(true);
    610          int xleft = 0;
    611          cell.setBounds(xleft + xpixelCell[ixColumn], yPix, xpixelCell[ixColumn+1] - xpixelCell[ixColumn], linePixel);
    612          ixColumn +=1;
    613        }
    614        yPix += linePixel;
    615      } else {
    616        //invisible cells:
    617        for(Text cell: row){
    618          cell.setVisible(false);
    619        }
    620      }
    621      ixrow +=1;
    622    }
    623    vScrollBar.setBounds(xyVscrollbar.x, xyVscrollbar.y, xyVscrollbar.dx, xyVscrollbar.dy);
    624  }
    625
    626  /**Invoked on {@link #swtKeyListener} if enter. */
    627  void setTextToLine(Text widgSwt){
    628    String text = widgSwt.getText();
    629    GralTable.CellData data = (GralTable.CellData)widgSwt.getData();
    630    SwtTable.this.setCellText(data, text);
    631  }
    632
    633
    634  protected String getCellText(GralTable.CellData cell){
    635    return cellsSwt[cell.ixCellLine][cell.ixCellColumn].getText();
    636  }
    637
    638
    639  @Override protected String getCellTextFocus(){
    640    if(cellInFocus == null) return null;
    641    else return cellInFocus.getText();
    642  }
    643
    644
    645
    646
    647
    648
    649  /**Focus listener implementation for all cells.
    650   * This routine is invoked whenever the focus of any Text field of the table will be lost the focus.
    651   * It invokes {@link GralTable.GraphicImplAccess#focusLostTable()} but only if
    652   * {@link GralTable.GraphicImplAccess#bRedrawPending} is not set. That prevents invocation while
    653   * {@link GralTable.GraphicImplAccess#updateGraphicCellContent()} sets the focus while updating the graphic cells.
    654   *
    655   */
    656  @Override public void focusLost(FocusEvent ev){
    657    //System.out.println("Cell focus lost");
    658    super.focusLostTable();
    659    if(!bRedrawPending){
    660      //System.out.println("SwtTable - cell focus lost;" + (SwtTable.this).outer.toString());
    661      GralTable.CellData celldata = (GralTable.CellData)ev.widget.getData();
    662      Text widgSwt = (Text)ev.widget;
    663      if(super.bColumnEditable(celldata.ixCellColumn)){
    664        String sText = widgSwt.getText();
    665        super.checkAndUpdateText(sText, celldata);
    666      }
    667      //widgSwt.setBackground(colorBackSelectNonFocusedSwt);
    668      System.out.println("SwtTableCell - focus lost;");
    669      //int iCellLine = data.ixCellLine; //ixLineNew - ixLine1;
    670      //for(int iCellCol = 0; iCellCol < zColumn(); ++iCellCol){
    671
    672        //cellSwt.setBackground(colorBackSelectNonFocusedSwt);
    673      //}
    674    }
    675  }
    676
    677  /**Focus listener implementation for all cells.
    678   * This routine is invoked especially if the mouse is landing
    679   * on a Text-field with click. Then this table line and column
    680   * should be selected as currently. <br>
    681   * This routine is invoked on setFocus()-call too. In this case
    682   * it should not done anything. The variable #bRedrawPending guards it.
    683   * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
    684   */
    685  @Override public void focusGained(FocusEvent ev) {
    686    SwtTable.this.focusGainedTable();
    687    setFocused(widgg, true);
    688    cellInFocus = (Text)ev.getSource();
    689    //System.out.println("SwtTableCell - focus gained;");
    690  }
    691
    692
    693
    694
    695
    696  protected void initSwtTable(Composite swtTable, int zColumns, SwtMng mng){
    697    int yPix = 0;
    698    Font font = mng.propertiesGuiSwt.getTextFontSwt(2, outer.whatIs, outer.whatIs);
    699    Color colorBackTableSwt = mng.getColorImpl(colorBackTable());
    700    for(int iCol = 0; iCol < zColumns; ++iCol){
    701      //menuColumns[iCol] = new SwtMenu(name + "_menu" + iCol, this, itsMng());
    702    }
    703    //NOTE: only if the swtSelectText is created first, it will drawn in in the foreground of the other cells.
    704    swtSearchText = new Text(swtTable, SWT.LEFT | SWT.SINGLE | SWT.READ_ONLY);
    705    swtSearchText.setFont(font);
    706    swtSearchText.setVisible(false);
    707    for(int iRow = 0; iRow < cellsSwt.length; ++iRow){
    708      for(int iCol = 0; iCol < zColumns; ++iCol){
    709        boolean editable = super.bColumnEditable(iCol);
    710        Text cell = new Text(swtTable, SWT.LEFT | SWT.SINGLE | (editable  ? 0 : SWT.READ_ONLY));
    711        if(editable){
    712          cell.addKeyListener(swtKeyListener);
    713        }
    714        cell.setFont(font);
    715        cell.addKeyListener(myKeyListener);
    716        cell.addFocusListener(this);
    717        cell.addMouseListener(mousePressedListener);
    718        cell.addMouseWheelListener(mouseWheelListener);
    719        cell.addTraverseListener(traverseListenerTable);
    720        GralTable.CellData cellData = new GralTable.CellData(iRow, iCol);
    721        cell.setData(cellData);
    722        cells[iRow][iCol] = cellData;
    723        int xdPixCol = super.columnPixel[iCol+1] - columnPixel[iCol];
    724        cell.setBounds(columnPixel[iCol], yPix, xdPixCol, linePixel);
    725        cell.setBackground(colorBackTableSwt);
    726        //cell.setMenu((Menu)menuColumns[iCol].getMenuImpl());
    727        cellsSwt[iRow][iCol] = cell;
    728      }
    729      yPix += linePixel;
    730    }
    731    //The text field for selection string
    732
    733
    734
    735  }
    736
    737
    738
    739
    740  /**The widget for the vertical scroll bar is a canvas with a special paint routine.
    741   * In the paint routine {@link SwtTable#paintVscrollbar(PaintEvent, Canvas)} is called.
    742   */
    743  @SuppressWarnings("synthetic-access")
    744  private class Vscrollbar extends Canvas
    745  {
    746    Vscrollbar(Composite parent) {
    747      super(parent, 0);
    748      addPaintListener(vScrollbarPainter);
    749
    750    }
    751
    752    @Override
    753    public void drawBackground(GC g, int x, int y, int dx, int dy) {
    754      System.out.println("VScrollbar - draw");
    755      //SwtTable.this.paintVscrollbar(g, Vscrollbar.this);
    756    }
    757
    758    private PaintListener vScrollbarPainter = new PaintListener(){
    759      @Override public void paintControl(PaintEvent e) {
    760        //System.out.println("VScrollbar - paintlistener");
    761        SwtTable.this.paintVscrollbar(e.gc, Vscrollbar.this);
    762      }
    763    };
    764
    765  }
    766
    767
    768
    769  /**The SWT-Composite for the cell texts and the scroll bar. */
    770  private class Table extends Composite {
    771
    772    public Table(Composite parent, int zColumns, SwtMng mng) {
    773      super(parent, 0);
    774    }
    775
    776
    777    //@Override public void drawBackground (GC gc, int x, int y, int width, int height, int offsetX, int offsetY) {
    778    //  redrawGthread();
    779    //}
    780
    781
    782
    783
    784
    785  }
    786
    787
    788
    789  protected void keyPressed(KeyEvent keyEv){
    790    try{
    791      if((keyEv.keyCode & 0xffff) !=0){
    792        final int keyCode = SwtGralKey.convertFromSwt(keyEv.keyCode, keyEv.stateMask, keyEv.character);
    793        processKeys(keyCode);
    794      }
    795    } catch(Exception exc){
    796      String txt = Assert.exceptionInfo("SwtTable - keyPressed Exception", exc, 0, 20, true).toString();
    797      swtWidgHelper.mng.mng.log.sendMsg(0, txt);
    798      //CharSequence stackInfo = Assert.exceptionInfo("Gral - SwtTable;", exc, 1, 5);
    799      //System.err.append(stackInfo);
    800      //exc.printStackTrace(System.out);
    801    }
    802
    803  }
    804
    805
    806
    807
    808  /**A Table is completed with a special key listener. On all keys
    809   * the {@link GralUserAction} given in the {@link GralWidget#getActionChange()} is called
    810   * <ul>
    811   * <li> with given command "table-key".
    812   * <li>params[0] is the selected line referenced with {@link GralTableLine_ifc}
    813   * <li>params[1] is the key code described in {@link KeyCode}
    814   * </ul>
    815   * If the method returns false, the central key action given in {@link GralMng#getRegisteredUserAction(String)}
    816   * for "keyAction" is tried to get and then invoked with cmd = "key" and the key code in params[0].
    817   * This central keyAction may be used for application centralized keys without association to the table itself.
    818   */
    819  class TableKeyListerner implements KeyListener
    820  {
    821    final KeyListener basicListener;
    822
    823
    824
    825    public TableKeyListerner(KeyListener basicListener)
    826    {
    827      this.basicListener = basicListener;
    828    }
    829
    830    @Override
    831    public void keyPressed(KeyEvent keyEv)
    832    { SwtTable.this.keyPressed(keyEv);
    833    }
    834
    835    @Override
    836    public void keyReleased(KeyEvent arg0)
    837    {
    838      //basicListener.keyReleased(arg0);
    839
    840    }
    841
    842  };
    843
    844  ControlListener resizeListener = new ControlListener()
    845  { @Override public void controlMoved(ControlEvent e)
    846    { //do nothing if moved.
    847      stop();
    848    }
    849
    850    /**The Composite for the whole table is resized already. This routine should determine which Text fields are visible and used.
    851     * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
    852     */
    853    @Override public void controlResized(ControlEvent e)
    854    {
    855      stop();
    856      Rectangle boundsTable = ((Control)e.widget).getBounds();
    857      int borderTable = ((Control)e.widget).getBorderWidth();
    858      //the pixTable is given already with the Composite of the whole table.
    859      GralRectangle pixTable = new GralRectangle(boundsTable.x, boundsTable.y, boundsTable.width, boundsTable.height);
    860      //it calls repaint with delay.
    861      //Composite parent = ((Control)e.widget).getParent();
    862      //Rectangle parentBounds = parent.getBounds();
    863      //GralRectangle pixTable = outer.pos().calcWidgetPosAndSize(itsMng().propertiesGui(), parentBounds.width, parentBounds.height, 0, 0);
    864      //GralRectangle pixTable = outer.pos().calcWidgetPosAndSize(itsMng().propertiesGui(), boundsTable.width, boundsTable.height, 0, 0);
    865      SwtTable.this.resizeTable(pixTable);
    866    }
    867
    868  };
    869
    870
    871  private final class MousePressedListenerTable implements MouseListener{
    872
    873    protected MousePressedListenerTable(){}
    874
    875
    876    @Override
    877    public void mouseDoubleClick(MouseEvent ev)
    878    { //Note: redirect to environment class to prevent usage of synthetic accessor
    879      SwtTable.this.mouseDouble(ev);
    880      //System.out.println("SwtTable-mouse-double");
    881
    882    }
    883
    884    @Override
    885    public void mouseDown(MouseEvent ev)
    886    { //Note: redirect to environment class to prevent usage of synthetic accessor
    887      SwtTable.this.mouseDown(ev);
    888    }
    889
    890    @Override
    891    public void mouseUp(MouseEvent ev)
    892    { //Note: redirect to environment class to prevent usage of synthetic accessor
    893      SwtTable.this.mouseUp(ev);
    894    }
    895
    896  }
    897
    898
    899  class MouseWheelListenerTable implements MouseWheelListener{
    900
    901    @Override
    902    public void mouseScrolled(MouseEvent e) {
    903      //System.out.println("SwtTable mouseWheel " + e.count);
    904      if(e.count >0){
    905        processKeys(KeyCode.mouseWheelUp);
    906      } else {
    907        processKeys(KeyCode.mouseWheelDn);
    908      }
    909    }
    910
    911  }
    912
    913
    914  /**Debug: this focus listener is not invoked any time. It is associated to a swt.Composite.
    915   *
    916   */
    917  private class FocusListenerTable implements FocusListener //extends SwtWidgetMng.SwtMngFocusListener
    918  {
    919    FocusListenerTable(SwtMng mng){
    920      //mng.super();
    921    }
    922
    923    @Override public void focusLost(FocusEvent e){
    924      System.out.println("SwtTable - debug;table composite focus lost. ");
    925    }
    926
    927    @Override public void focusGained(FocusEvent ev)
    928    { System.out.println("SwtTable - debug;table composite focus gained. ");
    929    }
    930
    931  };
    932
    933  TraverseListener traverseListenerTable = new TraverseListener() {
    934
    935    @Override public void keyTraversed(TraverseEvent ev)
    936    {
    937      Debugutil.stop();
    938      final int keyCode = SwtGralKey.convertFromSwt(ev.keyCode, ev.stateMask, ev.character);
    939      if(keyCode == KeyCode.tab) { // || keyCode == KeyCode.ctrl+KeyCode.tab) {
    940      //if(e.character == '\t'){
    941        //only the tab key should be handled because up and down are received by the key handler
    942        SwtTable.this.keyPressed(ev);
    943      }
    944    }
    945  };
    946
    947
    948
    949  /**An instance of this is associated to any cell of the table.
    950   * The focus gained method is used to set the current cell of the table and to invoke redraw
    951   * to show the new selection. That focus event is invoked if the mouse button is pressed on that cell.
    952   */
    953  private class XXXFocusListenerCell implements FocusListener
    954  {
    955
    956    /**This method does nothing. A lost focus color showing may be forced from redraw of another
    957     * table. Think about: maybe any action?
    958     * Old: This routine is invoked whenever the focus of any Text field of the table will be lost
    959     * the focus. Before that occurs, the field is the selected line, because it has had the focus.
    960     * Therefore the {@link GralTable#colorBackSelectNonFocused} is set.
    961     * @deprecated unnecessary yet.
    962     */
    963    @Deprecated
    964    @Override public void focusLost(FocusEvent ev){
    965      //System.out.println("Cell focus lost");
    966      SwtTable.this.focusLostTable();
    967      if(!bRedrawPending){
    968        //System.out.println("SwtTable - cell focus lost;" + (SwtTable.this).outer.toString());
    969        GralTable.CellData data = (GralTable.CellData)ev.widget.getData();
    970        Control widgSwt = (Control)ev.widget;
    971        //widgSwt.setBackground(colorBackSelectNonFocusedSwt);
    972        System.out.println("SwtTableCell - focus lost;");
    973        int iCellLine = data.ixCellLine; //ixLineNew - ixLine1;
    974        for(int iCellCol = 0; iCellCol < zColumn(); ++iCellCol){
    975          Text cellSwt = cellsSwt[iCellLine][iCellCol];
    976
    977          //cellSwt.setBackground(colorBackSelectNonFocusedSwt);
    978        }
    979      }
    980    }
    981
    982    /**This routine is invoked especially if the mouse is landing
    983     * on a Text-field with click. Then this table line and column
    984     * should be selected as currently. <br>
    985     * This routine is invoked on setFocus()-call too. In this case
    986     * it should not done anything. The variable #bRedrawPending guards it.
    987     * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
    988     */
    989    @Override public void focusGained(FocusEvent ev) {
    990      SwtTable.this.focusGainedTable();
    991      //System.out.println("SwtTableCell - focus gained;");
    992    }
    993
    994  };
    995
    996
    997
    998  /**This class wraps the {@link GralUserAction} for a menu action for the table.
    999   */
    1000  static class ActionUserMenuItem implements SelectionListener
    1001  {
    1002    final GralUserAction action;
    1003
    1004    public ActionUserMenuItem(GralUserAction action)
    1005    { this.action = action;
    1006    }
    1007
    1008    @Override
    1009    public void widgetDefaultSelected(SelectionEvent e) {
    1010      // TODO Auto-generated method stub
    1011
    1012    }
    1013
    1014    @Override
    1015    public void widgetSelected(SelectionEvent e)
    1016    { Object oWidgSwt = e.getSource();
    1017      final GralWidget widgg;
    1018      if(oWidgSwt instanceof Widget){
    1019        Widget widgSwt = (Widget)oWidgSwt;
    1020        Object oGralWidg = widgSwt.getData();
    1021        if(oGralWidg instanceof GralWidget){
    1022          widgg = (GralWidget)oGralWidg;
    1023        } else { widgg = null; }
    1024      } else { widgg = null; }
    1025      action.userActionGui(KeyCode.menuEntered, widgg);
    1026    }
    1027  }
    1028
    1029
    1030
    1031  protected SwtKeyListener swtKeyListener = new SwtKeyListener(widgg.gralMng()._impl.gralKeyListener)
    1032  {
    1033
    1034    @Override public final boolean specialKeysOfWidgetType(int key, GralWidget_ifc widgg, Object widgImpl){
    1035      boolean bDone = true;
    1036      if(KeyCode.isWritingKey(key)){
    1037        //bTextChanged = true;
    1038      }
    1039      if(key == KeyCode.enter) { // && KeyCode.isWritingOrTextNavigationKey(key)){
    1040        bDone = true;
    1041        assert(widgImpl instanceof Text);
    1042        Text widgSwt = (Text)widgImpl;
    1043        setTextToLine(widgSwt);
    1044      } else {
    1045        /*
    1046        boolean bUserOk;
    1047        if(user !=null){
    1048          Point selection = textFieldSwt.getSelection();
    1049          bUserOk = user.userKey(key
    1050              , textFieldSwt.getText()
    1051              , textFieldSwt.getCaretPosition()
    1052              , selection.x, selection.y);
    1053        } else bUserOk = false;
    1054        if(!bUserOk ){
    1055          switch(key){
    1056            case KeyCode.ctrl + 'a': {
    1057              textFieldSwt.selectAll();
    1058            } break;
    1059            default: bDone = false;
    1060          }
    1061        }
    1062        */
    1063      }
    1064      return bDone;
    1065    }
    1066  };
    1067
    1068
    1069
    1070  void stop(){}
    1071
    1072
    1073
    1074
    1075}