    001package org.vishia.gral.base;
    002
    003import java.io.BufferedReader;
    004import java.io.File;
    005import java.io.FileInputStream;
    006import java.io.FileReader;
    007import java.io.IOException;
    008import java.io.InputStream;
    009import java.text.DateFormat;
    010import java.text.SimpleDateFormat;
    011import java.util.ArrayList;
    012import java.util.Arrays;
    013import java.util.Date;
    014import java.util.LinkedList;
    015import java.util.List;
    016import java.util.concurrent.atomic.AtomicInteger;
    017
    018import org.vishia.byteData.VariableAccessWithIdx;
    019import org.vishia.byteData.VariableAccess_ifc;
    020import org.vishia.byteData.VariableContainer_ifc;
    021import org.vishia.curves.WriteCurve_ifc;
    022import org.vishia.gral.ifc.GralColor;
    023import org.vishia.gral.ifc.GralCurveViewTrack_ifc;
    024import org.vishia.gral.ifc.GralCurveView_ifc;
    025import org.vishia.gral.ifc.GralSetValue_ifc;
    026import org.vishia.gral.ifc.GralUserAction;
    027import org.vishia.gral.ifc.GralWidget_ifc;
    028import org.vishia.mainCmd.Report;
    029import org.vishia.mainCmd.ReportWrapperLog;
    030import org.vishia.util.Assert;
    031import org.vishia.util.Debugutil;
    032import org.vishia.util.KeyCode;
    033import org.vishia.util.Removeable;
    034import org.vishia.util.Timeshort;
    035import org.vishia.zbnf.ZbnfJavaOutput;
    036import org.vishia.zbnf.ZbnfParser;
    037
    038
    039/**Curve representation for timed values. It is the base class for all implementation.
    040 *
    041 * <ul>
    042 * <li> {@link #setSample(float[], int)}: Write out with new measured values.
    043 * </ul>
    044 * @see GralCurveViewTrack_ifc.
    045 * @author Hartmut Schorrig
    046 *
    047 */
    048public class GralCurveView extends GralWidget implements GralCurveView_ifc
    049{
    050
    051  /**Version, history and license.
    052   * <ul>
    053   * <li>2018-09-20 Hartmut improved for using in InspcCurveViewApp.
    054   *   <ul>
    055   *   <li>initTrack(...) renamed to {@link #addTrack(String, String, GralColor, int, int, float, float)}
    056   *   <li>{@link #searchTrack(String)} if data are read from csv for existing configuration.
    057   *   <li>{@link #readCurve(File)} now expects names of variable in the first line in any case, not specific format.
    058   *   <li>{@link #readCurveCsvHeadline(File)} can replace the correct variables in the configuration or builds new tracks
    059   *     for non existing configurated tracks.
    060   *   </ul>
    061   * <li>2016-07-03 Hartmut chg: {@link GraphicImplAccess} as base class of the implementing class SwtCurveView adequate to the  new concept:
    062   *   An implementing widget is derived from its derived class of {@link GralWidget.ImplAccess}. Therefore only that base class implements the GralWidgetImpl_ifc.
    063   * <li>2016-03-06 Hartmut chg: {@link #refreshFromVariable(VariableContainer_ifc)}: Handling of timeshort: If the timeshort starts with a lesser value
    064   *   it seems to be a new simulation starting with 0. Then the time difference to the last simulation is calculated and added to the timeshort.
    065   *   It sets a new {@link #setTimePoint(long, int, float)} to prevent overflow later. Initially or if {@link #cleanBuffer()} was invoked the timeshort counts from 0.
    066   *   Therewith the simulation results have an exact relative time but with the absolute timestamp of the PC's time though the simulation does not supply an absolute time.
    067   *   Before that change a bug is detected in {@link #prepareIndicesDataForDrawing(int, int, int, boolean)}: If the buffer from right side contains a greater short time,
    068   *   the indices return only 1 point. It is a bug in this function, not corrected yet because there are not greater times from right to left with the change above.
    069   *   TODO fix it though.
    070   * <li>2016-01-24 Hartmut chg: {@link CommonCurve#lastTimeShort}: Don't store points with the same time stamp.
    071   * <li>2016-01-24 Hartmut bugfix: {@link #applySettings(String)} after read cfg: remove previous {@link CommonCurve#timeVariable}, may be a new one!
    072   * <li>2015-07-12 Hartmut new: {@link Track#setVisible(int)} to control the visibility of tracks.
    073   * <li>2015-07-12 Hartmut new: {@link Track#groupTrackScale(GralCurveViewTrack_ifc)} for groups of tracks with same scaling.
    074   * <li>2014-05-20 Hartmut new in {@link #setSample(float[], int)}: write a point only if at least one variable was refreshed.
    075   * <li>2014-03-14 Hartmut new: {@link CommonCurve#timeVariable} for time from target.
    076   * <li>2014-02-03 Hartmut new: {@link CommonCurve#bFreeze}: freeze as common property of more as one GralCurveView. Constructor argument.
    077   * <li>2014-01-29 Hartmut new: Comment in Datapath supported. For nice presentation in list on long variable paths.
    078   * <li>2013-11-19 Hartmut new: {@link #repaint(int, int)} overridden forces paint of the whole curve
    079   *   by setting {@link #bPaintAllCmd}, whereby in {@link #setSample(float[], int)} super.repaint is invoked,
    080   *   which does not set {@link #bPaintAllCmd} and paints therefore only the new data.
    081   * <li>2013-07-25 Hartmut new AutoSave capabilities.
    082   * <li>2013-05-19 Hartmut new: {@link GralCurveViewMouseAction}
    083   * <li>2013-05-19 Hartmut new: {@link #selectTrack(int, int, int, int)} with ctrl and left mouse pressed
    084   * <li>2013-05-14 Hartmut new: {@link Track#getIxTrack()}
    085   * <li>2013-03-27 Hartmut bugfix: The {@link #bNewGetVariables} have to be set in {@link #setDataPath(String)} and
    086   *   {@link #applySettings(String)} because this methods sets a new variable which should be searched.
    087   *   All other changes are gardening and comments.
    088   * <li>2013-01-25 Hartmut improved: Error message in {@link #refreshFromVariable(VariableContainer_ifc)} only on
    089   *   paint-complete of the view. If the implementation receives a paintAll command by mouseClick on the graphic
    090   *   or because the graphic is shown the first time, the {@link #setPaintAllCmd()} is invoked there. The variable
    091   *   {@link #bNewGetVariables} is set, that causes the error message.
    092   *   Before: no error message was created, the error was obscure.
    093   * <li>2012-08-11 Hartmut now grid with  timestamps
    094   * <li>2012-06-08 Hartmut new: {@link #applySettings(String)} and {@link #writeSettings(Appendable)} for saving
    095   *   and getting the configuration of curve view from a file or another text.
    096   * <li>2012-04-01 Hartmut new: Using {@link VariableAccessWithIdx} to access values.
    097   * <li>2012-03-25 Hartmut chg: Some routines from SWT moved to this because there are independent.
    098   * <li>2012-03-17 Hartmut chg: All track-associated data now in the {@link Track} inner class.
    099   * <li>2012-02-25 Hartmut new: All data have a short timestamp. The x-pixel are calculated with timestamp,
    100   *   not only with currently storage.
    101   * <li>2012-02-25 Hartmut new: Zoom functinality with timestamp and cursors
    102   * <li>2012-02-21 Hartmut Now the CurveView works in the new environment. Some adjustments necessary yet.
    103   * <li>2011-06-00 Hartmut New concept of GralWidget etc and new Configuration concept
    104   *   with {@link org.vishia.gral.cfg.GralCfgBuilder}. The old GuiDialogZbnfControlled.class
    105   *   was not use nevermore. But the CurveView was not adapted for that.
    106   * <li>2010-03-00 Hartmut The curve view was development as basic feature.
    107   * </ul>
    108   * <br><br>
    109   * <b>Copyright/Copyleft</b>:
    110   * For this source the LGPL Lesser General Public License,
    111   * published by the Free Software Foundation is valid.
    112   * It means:
    113   * <ol>
    114   * <li> You can use this source without any restriction for any desired purpose.
    115   * <li> You can redistribute copies of this source to everybody.
    116   * <li> Every user of this source, also the user of redistribute copies
    117   *    with or without payment, must accept this license for further using.
    118   * <li> But the LPGL is not appropriate for a whole software product,
    119   *    if this source is only a part of them. It means, the user
    120   *    must publish this part of source,
    121   *    but doesn't need to publish the whole source of the own product.
    122   * <li> You can study and modify (improve) this source
    123   *    for own using or for redistribution, but you have to license the
    124   *    modified sources likewise under this LGPL Lesser General Public License.
    125   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    126   * </ol>
    127   * If you intent to use this source without publishing its usage, you can get
    128   * a second license subscribing a special contract with the author.
    129   *
    130   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    131   *
    132   */
    133  public final static int version = 20130327;
    134
    135
    136
    137
    138  public class CommonCurve {
    139
    140    /**If true, then the display is freezed.
    141     */
    142    public boolean bFreeze = false;
    143
    144
    145    /**If set, then use this variable to get the short time.
    146     *
    147     */
    148    public VariableAccess_ifc timeVariable;
    149
    150    /**If set, the {@link #timeVariable} will be initialized.
    151     *
    152     */
    153    public String timeDatapath;
    154
    155
    156
    157  }
    158
    159
    160
    161
    162
    163
    164  /**Instances of this class were be created only temporary while transfer a parse result of {@link GralCurveView#applySettings(String)}
    165   * The instance holds values for {@link ZbnfJavaOutput} to apply to a track
    166   * calling {@link GralCurveView#addTrack(String, String, GralColor, int, int, float, float)}
    167   * inside {@link GralCurveView.ZbnfSetCurve#add_Track(ZbnfSetTrack)}.
    168   */
    169  public static class ZbnfSetTrack {
    170    /**From Zbnf component with semantic <?name>. */
    171    public String name;
    172    /**From Zbnf component with semantic <?datapath>. */
    173    public String datapath;
    174    /**Color and style from Zbnf component with semantic <?color>. */
    175    GralColor color_; int style_ = 0;
    176    /**From Zbnf component with semantic <?nullLine>. */
    177    public int nullLine;
    178    /**From Zbnf component with semantic <?scale>. */
    179    public float scale;
    180    /**From Zbnf component with semantic <?offset>. */
    181    public float offset;
    182    /**From Zbnf component with semantic <?color>. */
    183    public void set_color(String color){ color_ = GralColor.getColor(color.trim()); }
    184  }
    185
    186
    187
    188  /**The instance of this class {@link GralCurveView#zbnfSetCurve} will be used only temporary while transfer a parse result
    189   * of {@link GralCurveView#applySettings(String)}
    190   * The instance contains only the capabilty to get the track info from the parse result
    191   * and to call {@link GralCurveView#addTrack(String, String, GralColor, int, int, float, float)}
    192   * inside {@link #add_Track(ZbnfSetTrack)}.
    193   */
    194  public class ZbnfSetCurve{
    195    /**From Zbnf component with semantic <?Track>. */
    196    public ZbnfSetTrack new_Track(){
    197      return new ZbnfSetTrack();
    198    }
    199    /**From Zbnf component with semantic <?Track>. */
    200    public void add_Track(ZbnfSetTrack track){
    201      addTrack(track.name, track.datapath, track.color_, track.style_, track.nullLine, track.scale, track.offset);
    202    }
    203
    204    public void set_timeDatapath(String val){
    205      GralCurveView.this.common.timeDatapath = val;
    206    }
    207
    208  }
    209
    210  final ZbnfSetCurve zbnfSetCurve = new ZbnfSetCurve();
    211
    212
    213  /**Instances of this class contain the scaling for one ore more tracks.
    214   * One instance if referenced from maybe one or more as one track.
    215   */
    216  public static class TrackScale
    217  {
    218    /**The scale for 10 percent of view without zoom.. */
    219    public float yScale;
    220
    221    /**The value of input data which is shown at the 0-line. */
    222    public float yOffset;
    223
    224    //public float yFactor;
    225
    226    /**The percent from 0..100 where the 0-line is presented. */
    227    public int y0Line;
    228
    229
    230    @Override public TrackScale clone(){
    231      try{ return (TrackScale)super.clone(); }
    232      catch(CloneNotSupportedException exc){
    233        TrackScale ret = new TrackScale();
    234        ret.yScale = this.yScale;
    235        ret.yOffset = this.yOffset;
    236        ret.y0Line = this.y0Line;
    237        return ret;
    238      }
    239    }
    240  }
    241
    242
    243  /**The describing and the actual data of one track (one curve)
    244   */
    245  public static class Track implements GralCurveViewTrack_ifc, GralSetValue_ifc {
    246    public final String name;
    247
    248    /**The index of the track in the List of tracks.
    249     * It is the correspondent index in the float parameter for {@link GralCurveView#setSample(float[], int)}
    250     */
    251    public final int ixList;
    252
    253    private final GralCurveView outer;
    254
    255    public String sDataPath;
    256
    257    public VariableAccess_ifc variable;
    258
    259    private Object oContent;
    260
    261    /**Index of a variable
    262     *
    263     */
    264    private int dataIx;
    265
    266    public float YYYactValue;
    267
    268    public float min, max;
    269
    270    /**Reference to the scaling to show the track. More as one track can build a scale group
    271     * which refers the same instance. */
    272    public TrackScale scale;
    273
    274    /**The color of the line. */
    275    public GralColor lineColor;
    276
    277    /**The brightness of the line. It is used to show the selected line. */
    278    public int lineWidth = 1;
    279
    280
    281    /**The state to show. 0=hidden, don't show, but the values are stored.
    282     * 1= show normal. 2= show lifted out (selected).
    283     */
    284    public int showSelected = 1;
    285    //public float y0Pix;
    286
    287    /**Array stores the last values which are able to show. */
    288    public float[] values;
    289
    290    /**last values for paint.
    291     * The current paint goes from lastValueY[1] to the current point.
    292     * The lastValueY[0] is used to repaint the last curve peace while shifting draw content.
    293     */
    294    protected final int[] XXXlastValueY = new int[2];
    295
    296    int identLastSelect;
    297
    298    /**The value from last draw, do not calculate twice. */
    299    public int ypixLast;
    300
    301    public Track(GralCurveView outer, String name, int ixList){
    302      this.outer = outer; this.name = name; this.ixList = ixList; }
    303
    304    @Override public void setContentInfo(Object content) { oContent = content; }
    305
    306    @Override public Object getContentInfo() { return oContent;  }
    307
    308    @Override public void setDataPath(String sDataPath) {
    309      this.sDataPath = sDataPath;
    310      this.variable = null;
    311      outer.bNewGetVariables= true;  //force searching the variable
    312    }
    313
    314    @Override public String getDataPath() { return sDataPath; }
    315
    316    @Override public int getDataIx() { return dataIx; }
    317
    318    @Override public void setDataIx(int dataIx) { this.dataIx = dataIx; }
    319
    320    @Override public void setValue(float value) { this.YYYactValue = value; }
    321
    322    @Override public void setLongValue(long value) { this.YYYactValue = value; }
    323
    324    @Override public void setValue(Object[] value) { } //TODO this.actValue = value[0]; }
    325
    326    /**Maybe used for scaling, yet unused here.
    327     * @see org.vishia.gral.ifc.GralSetValue_ifc#setMinMax(float, float)
    328     */
    329    @Override public void setMinMax(float minValue, float maxValue) {
    330      this.min = minValue; this.max = maxValue;
    331    }
    332
    333    @Override public int getLinePercent(){ return scale.y0Line; }
    334
    335    @Override public float getOffset(){ return scale.yOffset; }
    336
    337    @Override public float getScale7div(){ return scale.yScale;  }
    338
    339    @Override public GralColor getLineColor(){ return lineColor; }
    340
    341
    342    /**Change the scaling of a track.
    343     * @param trackNr Number of the track in order of creation, 0 ist the first.
    344     * @param scale7div value per division
    345     * @param offset value, which is shown at line0
    346     * @param line0 percent of 0-line in graphic.
    347     */
    348    public void setTrackScale(float scale7div, float offset, int line0){
    349      scale.yScale = scale7div;
    350      scale.yOffset = offset;
    351      scale.y0Line = line0;
    352    }
    353
    354
    355    /**Sets the scaling to sharing with another track. Changing one of this tracks with
    356     * {@link #setTrackScale(float, float, int)} influences all shared tracks immediately.
    357     * All shared tracks refer the same instance of {@link TrackScale}.
    358     * @param from
    359     */
    360    @Override public void groupTrackScale(GralCurveViewTrack_ifc from){ scale = ((Track)from).scale; }
    361
    362    @Override public boolean isGroupedTrackScale(GralCurveViewTrack_ifc with){
    363      if(with == null) return false;
    364      return scale == ((Track)with).scale;
    365    }
    366
    367    /**Sets the scaling to a own instance of {@link TrackScale}. The track is independent now. */
    368    @Override public void ungroupTrackScale(){
    369      TrackScale oldScale = this.scale;
    370      this.scale = oldScale.clone();
    371    }
    372
    373    @Override public float getValueCursorLeft(){ return getValueCursor(((GraphicImplAccess)outer._wdgImpl).xpCursor1); }
    374
    375
    376
    377    @Override public float getValueCursorRight(){ return getValueCursor(((GraphicImplAccess)outer._wdgImpl).xpCursor2); }
    378
    379
    380    private float getValueCursor(int cursor){
    381      float value; ///
    382      if(cursor >=0 && cursor < ((GraphicImplAccess)outer._wdgImpl).ixDataShown.length){
    383        try{
    384          int ixData = ((GraphicImplAccess)outer._wdgImpl).getIxDataFromPixelRight(cursor);
    385          value = values[ixData];
    386        } catch(Exception exc){
    387          value = 77777.7f;
    388        }
    389      } else {
    390        value = 777777.7f;
    391      }
    392      return value;
    393    }
    394
    395
    396
    397    @Override public float getValueLast(){ return 0; }
    398    @Override public float getValueMin(){ return 0; }
    399    @Override public float getValueMax(){ return 0; }
    400
    401
    402
    403
    404
    405    /**
    406     * @see org.vishia.gral.ifc.GralCurveViewTrack_ifc#setLineProperties(org.vishia.gral.ifc.GralColor, int, int)
    407     */
    408    @Override public void setLineProperties(GralColor color, int width, int pattern){
    409      if(color !=null) { lineColor = color; }
    410      if(width >0) { lineWidth = width;}
    411    }
    412
    413    @Override public void setVisible(int mode){ showSelected = mode; }
    414
    415    @Override public int getVisible(){ return showSelected; }
    416
    417
    418
    419
    420    @Override public void setText(CharSequence text){
    421      System.err.println("GralCurveView - setText not supported; Widget = " + name + "; text=" + text);
    422    }
    423
    424
    425
    426    @Override public String toString(){ return "Track: " + name + "(" + sDataPath + "," + (variable !=null ? variable.toString(): "variable = null") + ")"; }
    427
    428  }
    429
    430  /**Inner class for time organisation. */
    431  public static class TimeOrganisation{
    432
    433    Timeshort absTime = new Timeshort();
    434
    435    /**It is counted while the pair {@link #absTime_short} and {@link #absTime} is set newly.
    436     * The pair is consistent only if this counter is the same before and after read.
    437     */
    438    private volatile int ctTimeSet;
    439
    440    /**The last timeshort from the timeVariable. To detect newly simulation if it starts with a lesser value. */
    441    int timeshortLast;
    442
    443    /**Value to add to the time stamp to get a continuous time for some simulations one after another. */
    444    int timeshortAdd;
    445
    446    public int lastShortTimeDateInCurve;
    447
    448    /**Short time stamp of the oldest stored point. */
    449    public int firstShortTimeDateInCurve;
    450
    451    public int nrofPixelForTimestep;
    452
    453    /**Division type.
    454     * <ul>
    455     * <li>'2': 100  2   4   6   8  10  12  14  16  18 120
    456     * <li>'1': 100 1  2  3  4  5  6  7  8  9  200
    457     * <li>'5': 100  5  10  15  20  25  30  35  40  45 150
    458     * </ul>
    459     */
    460    //public char divType;
    461
    462
    463    /**Number of pixel per 1 fine division in vertical lines. The time for 1 division is a rounded number.
    464     * The number of pixel have to be float, because it is cummulated. */
    465    float pixelPerTimeDiv, pixelPerTimeFineDiv;
    466
    467    //int divPerBoldDiv = 10;
    468
    469    /**Number of millisec between 2 fine divisions. It is a number of millisec able to divide by 10, 5 or 2. */
    470    int millisecPerDiv = 100, millisecPerFineDiv =10;
    471
    472    /**Number of shorttime units for 1 pixel.
    473     * A time unit may be 1 millisecond for currently showing curves.
    474     * This value may be e.g. 100 to show 30 seconds in 300 pixel or e.g. 10 to show 5 seconds in 500 pixel.
    475     * This value may be given in another unit for example 1 microseconds. It should be the same value unit
    476     * as used in {@link #setSample(float[], int)}.
    477     */
    478    public float timePerPixel = 1.0f;
    479
    480
    481    /**The reciprocal of {@link #timePerPixel}. The number of pixel for 1 short time step. It is less 1.0 often. */
    482    public float pixel7time = 1.0f;
    483
    484    /**Number of time shortTime steps to showing the curve in the current view.
    485     * If this value is given (>=0) then the {@link #timePerPixel} will be calculated from this
    486     * regarding the current graphic size.
    487     * This value should be given in the same value unit as used for {@link #setSample(float[], int)}.
    488     */
    489    public int timeSpread = 50000;
    490
    491    ///
    492    /**The last tested time to produce vertical lines for time division. */
    493    public int timeLeftShowing;
    494
    495    public long timeAbsOnLastStrongDiv;
    496
    497    //public String sTimeAbsSec;
    498
    499    /**Accumulated nr of pixel written with short drawing after a strong division.
    500     * It is used to write the number after a proper time.
    501     */
    502    public int pixelWrittenAfterStrongDiv;
    503
    504    /**Pixel position from right for fine divisions of time lines (vertical lines) and normal divisions.
    505     * This array is filled newly whenever any draw or paint action is done. It is prepared in the routine
    506     * {@link #prepareIndicesDataForDrawing(int, int, int)} and used in the draw routine of the implementation level.
    507     */
    508    public final int[] xPixelTimeDiv = new int[50], xPixelTimeDivFine = new int[200];
    509
    510
    511    /**The text written at the time divisions. It is a mm:ss, ss.SSS or hh::mm */
    512    public final String[] sTimeAbsDiv = new String[50];
    513
    514
    515    int[] millisecPerFineDivVariants = new int[]{  1,  2,  5,  10,  20,  50,  100,  200,  500,  1000,  2000,  5000,  10000,  20000,  60000,  120000,  600000, 1200000,  3600000, 18000000};
    516
    517
    518
    519    /**Milliseconds per division for number of pixel per fine division between 12..30.
    520     *
    521     */
    522    int[] millisecPerDivVariants =     new int[]{  5, 10, 20,  50, 100, 200,  500, 1000, 2000,  5000, 10000, 20000,  60000, 120000, 300000,  600000, 3600000, 7200000, 18000000, 72000000};
    523
    524    /**Calculates the divisions with known {@link #timePerPixel}.
    525     * This routine should be called whenever the display zoom will be changed.
    526     * It sets {@link #divType}, {@link #pixelPerTimeFineDiv}, {@link #millisecPerFineDiv}
    527     */
    528    public void calc(){
    529      int millisec20pixel = (int)(12 * timePerPixel * absTime.millisec7short());  //millisec for 12 pixel
    530      boolean bFound = false;
    531      for(int ii = 0; ii < millisecPerDivVariants.length; ++ii){  //search in [5 10 20 ...] etc. millisecPerDivisions
    532        if(millisecPerFineDivVariants[ii] >= millisec20pixel){
    533          millisecPerDiv = millisecPerDivVariants[ii];            //sets millisecPerDiv and ..FineDiv
    534          millisecPerFineDiv = millisecPerFineDivVariants[ii];
    535          bFound = true;
    536          break;
    537        }
    538      }
    539      if(bFound) {
    540        pixelPerTimeDiv =     millisecPerDiv     / (timePerPixel * absTime.millisec7short());  //number of pixel per division, should be appropriate 48..150
    541        pixelPerTimeFineDiv = millisecPerFineDiv / (timePerPixel * absTime.millisec7short());  //number of pixel per division, should be appropriate 12..30
    542      } else {
    543        //use a fix value to prevent any failure calculations.
    544        pixelPerTimeDiv = 150;
    545        pixelPerTimeFineDiv = 30;
    546      }
    547      /*
    548      double millisecExp20pixel = Math.log10(millisec20pixel);
    549      int millisecExpInt20pixel = (int)millisecExp20pixel;
    550      int milli10sec = (int)Math.pow(10, millisecExpInt20pixel);
    551      float pixelMilli10sec = milli10sec / timePerPixel;
    552      if(pixelMilli10sec < 4){
    553        divType = '5';
    554        pixelPerTimeFineDiv = 5 * pixelMilli10sec;
    555        millisecPerFineDiv = milli10sec * 5;
    556      } else if(pixelMilli10sec < 10){
    557        divType = '2';
    558        pixelPerTimeFineDiv = 2 * pixelMilli10sec;
    559        millisecPerFineDiv = milli10sec * 2;
    560      } else{ //10..20
    561        divType = '1';
    562        pixelPerTimeFineDiv = pixelMilli10sec;
    563        millisecPerFineDiv = milli10sec;
    564      }
    565      if(millisecPerFineDiv <=2000 ){
    566        millisecPerDiv = 10 * millisecPerFineDiv;  //up to 10 sec step
    567        pixelPerTimeDiv = 10 * pixelPerTimeFineDiv;
    568      } else if(millisecPerFineDiv <=20000 ){
    569        millisecPerDiv = 6 * millisecPerFineDiv;  //up to 10 sec step
    570        pixelPerTimeDiv = 6 * pixelPerTimeFineDiv;
    571      } else {
    572        millisecPerDiv = 10 * millisecPerFineDiv;  //minute steps.
    573        pixelPerTimeDiv = 10 * pixelPerTimeFineDiv;
    574      }
    575      */
    576    }
    577
    578  }
    579
    580  public TimeOrganisation timeorg = new TimeOrganisation();
    581
    582
    583  protected static class DataOrganisation
    584  {
    585    /**Index in the {@link Track#values} where the last auto write was done.
    586     * See {@link GralCurveView#timeInitAutoSave()}. */
    587    public int ixDataStartAutoSave, ixDataEndAutoSave;
    588
    589
    590    public int ixDataStartSave, ixDataEndSave;
    591
    592    /**The number of array elements which are used in {@link #ixDataShown} for the current view.
    593     * Either the display size is less, then this is less then 2000. It is the usual case.
    594     * Or the number of available data are less, the it is lesser than the pixel size of the curve.
    595     */
    596    protected int zPixelDataShown;
    597
    598
    599    /**Set to true if the data are wrapped in the buffer. If false, there are less data. */
    600    boolean bWrappedInBuffer;
    601
    602  }
    603
    604  protected final DataOrganisation dataOrg = new DataOrganisation();
    605
    606
    607
    608  protected static class SaveOrganisation{
    609
    610    /** Values to save for autosave. */
    611    public int nrofValuesAutoSave;
    612
    613
    614    public int ctValuesAutoSave;
    615
    616    //boolean bAutoSave;
    617  }
    618
    619  protected SaveOrganisation saveOrg = new SaveOrganisation();
    620
    621
    622
    623
    624  public static class PixelOrganisation
    625  {
    626    /**Size of the curve range in pixel. Set at any time on last draw action. */
    627    public int xPixelCurve, yPixelCurve;
    628
    629  }
    630
    631  /**All tracks to return for filling. It has the same members in the same order like {@link #listTracks}*/
    632  //protected final List<GralSetValue_ifc> listTrackSet = new LinkedList<GralSetValue_ifc>();
    633
    634
    635  public final CommonCurve common;
    636
    637
    638  /**True then saves values.  */
    639  public boolean bActive;
    640
    641
    642  /**Current number of values in the data field.
    643   * If less values are set ({@link #setSample(float[])},
    644   * then the nrofValues is less than values.length.
    645   * Else it is ==values.length. */
    646  public int nrofValues = 0;
    647
    648
    649  private boolean bNewGetVariables = true;
    650
    651
    652  /**A short timestamp for the values in {@link GralCurveView.Track#values}.
    653   * It maybe a millisecond timestamp. Then about 2000000 seconds are able to display.
    654   * This array and the {@link Track#actValue} array are filled wrapping.
    655   * <br><br>
    656   * A negative difference between 2 successive time stamps designates the time break point between newer values
    657   * and the successive currently points to the newest one in ascending direction (past to future).
    658   * Initially this array will be filled with successive values from 0 to negatives in step -1
    659   * to designate a time break at all points.
    660   * <br><br>
    661   * At startup of filling 3 data points this array is filled for example with
    662   * <pre>
    663   * -1234, -1245, -1256, -3, -4, -5, ...
    664   * </pre>
    665   * In this example the wrapping time stamps are negative but successive ascending of course.
    666   * The not used 4. point is accepted as a correct point faulty, it produces a presentation to the value 0
    667   * because nothing is stored in data in 1253 time units (which may be milliseconds). This faulty presentation
    668   * is only present on startup of graphic and only if the difference is in range of presentation width,
    669   * see {@link #prepareIndicesDataForDrawing(int, int)}
    670   */
    671  public final int[] timeValues;
    672
    673
    674  /**Distance of nrofValues for one vertical strong grid line.
    675   * This value is used to limit nrofValuesForGrid. */
    676  public int gridDistanceStrongY;
    677
    678
    679  /**Deepness of the storage of values to present.
    680   * It is a power of 2 anytime.
    681   */
    682  public final int maxNrofXValues;
    683
    684
    685  public boolean testStopWr;
    686
    687
    688  /**Write index of values, increment by {@link #setSample(float[], int)}.
    689   * The index refers to the last written value.
    690   * Initially it is -1, the first write will be increment ?? to prevent any exception while values are not set before.
    691   */
    692  public int ixDataWr;
    693
    694
    695  /**All tracks to return for filling. It has the same members in the same order like {@link #listTracks}*/
    696  //protected final List<GralSetValue_ifc> listTrackSet = new LinkedList<GralSetValue_ifc>();
    697
    698
    699  /**The number of shift right to get the numeric index in values. */
    700  public final int shIxiData;
    701
    702
    703  /**Mask of index in values. */
    704  public final int mIxiData;
    705
    706
    707  /**Mask of any ixData. */
    708  protected final int mIxData;
    709
    710
    711  public int newSamples;
    712
    713
    714  protected int nrofValuesForGrid;
    715
    716
    717  /**The increment step of ixData.
    718   * Concept of wrap around: The values[]-array are used wrap around, to prevent
    719   * expensive shift operations. The size of the array is a power of 2 anytime.
    720   * The index is hold in an integer variable, which wraps around in the bit space too.
    721   * To use cheap increment and compare functionality, the range for the index
    722   * is the full integer range. To step from one to next index, use this adding value.
    723   */
    724  public final int adIxData;
    725
    726
    727  /**All tracks. Anytime if the tracks are changed a new List is created. It is because the list can be used in another thread yet in an iterator. */
    728  public List<Track> listTracks = new ArrayList<Track>();
    729
    730
    731  protected final GralCurveViewMouseAction mouseAction = new GralCurveViewMouseAction();
    732
    733
    734  /**This action is called whenever a cursor position is changed. */
    735  private GralUserAction actionMoveCursor;
    736
    737
    738  /**This action is called whenever a track was selected. */
    739  private GralUserAction actionSelectTrack;
    740
    741
    742
    743
    744
    745  public GralCurveView(String sName, int maxNrofXvaluesP, CommonCurve common)
    746  {
    747    super(null, sName, 'c');
    748    this.common = common == null ? new CommonCurve() : common;
    749    int maxNrofXvalues1 = 1;
    750    int shIxData1 = 32;
    751    while(maxNrofXvalues1 < maxNrofXvaluesP){
    752      maxNrofXvalues1 <<=1;
    753      shIxData1 -=1;
    754    }
    755    this.shIxiData = shIxData1;
    756    this.maxNrofXValues = maxNrofXvalues1; //maxNrofXvalues;
    757    this.adIxData = 0x40000000 / (maxNrofXValues >>2);  //NOTE: integer division, all >>2
    758    this.mIxData = ~(this.adIxData -1); //all bits which have to be used, mask out lower bits.
    759    this.mIxiData = maxNrofXValues -1;  //e.g. from 0x1000 to 0xfff
    760    this.ixDataWr = -adIxData; //initial write position, first increment to 0.
    761    //
    762    timeValues = new int[maxNrofXValues];
    763    cleanBuffer();
    764    saveOrg.nrofValuesAutoSave = (int)(maxNrofXValues * 0.75);
    765    //values = new float[maxNrofXvalues][nrofTracks];
    766    //setPanelMng(mng);
    767    setActionMouse(mouseAction, 0);
    768
    769    //mng.registerWidget(this);
    770  }
    771
    772
    773
    774  public void cleanBuffer()
    775  {
    776    for(int ix = 0; ix < maxNrofXValues; ++ix){
    777      timeValues[ix] = ix;  //store succession of time values to designate it as empty.
    778    }
    779    timeorg.calc();
    780    timeorg.timeshortAdd = 0;
    781    timeorg.timeshortLast = 0;
    782    timeorg.absTime.clean();
    783    if(super._wdgImpl !=null) {
    784      GraphicImplAccess wdgi = (GraphicImplAccess)super._wdgImpl;
    785      wdgi.ixDataDraw = ixDataWr =0;
    786      wdgi.ixDataCursor1 = wdgi.ixDataCursor2 = 0;
    787      wdgi.ixDataShowRight = 0;
    788      Arrays.fill(wdgi.ixDataShown, 0);
    789    }
    790  }
    791
    792
    793
    794
    795  /**It will be called after construction of the implementation graphic in the derived ctor.
    796   *
    797   */
    798  public void initMenuContext(){
    799    GralMenu menuCurve = getContextMenu();
    800    //menuCurve.addMenuItemGthread("pause", "pause", null);
    801    menuCurve.addMenuItem("refresh", actionPaintAll);
    802    menuCurve.addMenuItem("go", actionGo);
    803    //menuCurve.addMenuItemGthread("zoomOut", "zoom in", null);
    804    menuCurve.addMenuItem("zoomBetweenCursor", "zoom between Cursors", actionZoomBetweenCursors);
    805    menuCurve.addMenuItem("zoomOut", "zoom out", actionZoomOut);
    806    menuCurve.addMenuItem("cleanBuffer", "clean Buffer", actionCleanBuffer);
    807    //menuCurve.addMenuItemGthread("zoomOut", "to left", null);
    808    //menuCurve.addMenuItemGthread("zoomOut", "to right", null);
    809
    810  }
    811
    812
    813  public CommonCurve getCommonData(){ return common; }
    814
    815  /**This action will be called whenever a cursor position is changed. */
    816  public void setActionMoveCursor(GralUserAction action){ actionMoveCursor = action; }
    817
    818
    819  /**This action will be called whenever a track was selected. The selection will be done by
    820   * pressing the left mouse with ctrl on a curve view point. */
    821  public void setActionTrackSelected(GralUserAction action){ actionSelectTrack = action; }
    822
    823
    824  /**Creates and adds a track of the curve view. Note: The current {@link #listTracks} instance won't be changed.
    825   * Instead, a new ArrayList is created here with the content of listTracks and the new track.
    826   * This is because the {@link #listTracks} instance may be used in a iterator in another thread.
    827   * That is in {@link #setSample(float[], int)} which can be invoked in a cyclically or receiving thread,
    828   * or in {@link #refreshFromVariable(VariableContainer_ifc)} which is invoked in the Inspector Gui cyclically
    829   * to get values from target. If during operation a track was add by the users operation on GUI, it should not disturb the current process.
    830   *
    831   *
    832   * This routine should be called only one time per track to create a new one.
    833   * in the order of tracks.
    834   * Note: renamed from initTrack, 2018-09
    835   * @param sNameTrack
    836   * @param sDataPath
    837   * @param color
    838   * @param style
    839   * @param nullLine
    840   * @param scale
    841   * @param offset
    842   * @return The track instance.
    843   */
    844  public Track addTrack(String sNameTrack, String sDataPath, GralColor color, int style
    845      , int nullLine, float scale, float offset)
    846  {
    847    //Track track = initTrack(sNameTrack, sDataPath, color, style, nullLine, scale, offset, listTracksNew);
    848    Track track = new Track(this, sNameTrack, listTracks.size());
    849    track.values = new float[this.maxNrofXValues];
    850    track.scale = new TrackScale();
    851    track.sDataPath =sDataPath;
    852    track.variable = null;
    853    track.scale.y0Line = nullLine;
    854    track.scale.yOffset = offset;
    855    track.scale.yScale = scale;
    856    track.lineColor = color == null ? GralColor.getColor("rd") : color;
    857    bNewGetVariables = true;  //to force re-read of all variables.
    858    //
    859    //add the track, but build a new List
    860    List<Track> listTracksNew = new ArrayList<Track>();
    861    listTracksNew.addAll(this.listTracks); //Atomic access, iterate in local referenced list.
    862    listTracksNew.add(track);
    863    listTracks = listTracksNew;
    864    return track;
    865  }
    866
    867
    868
    869
    870
    871
    872  /**Searches and returns the track from listTrack per name.
    873   * @param name
    874   * @return null if not found.
    875   */
    876  private Track searchTrack(String name) {
    877    for(Track e: listTracks) {
    878      if(e.sDataPath.equals(name)) return e;
    879    }
    880    return null;
    881  }
    882
    883
    884
    885  public void setTimePerPixel(int time){
    886    timeorg.timePerPixel = time;
    887  }
    888
    889
    890  public void setTimeSpread(int time){
    891    if(time <= 0) throw new IllegalArgumentException("GralCurveView.setTimeSpread - value should >0");
    892    timeorg.timeSpread = time;
    893  }
    894
    895
    896
    897  /**Returns the path of the time variable if given or null.
    898   */
    899  public String getTimeVariable(){ return common.timeDatapath; }
    900
    901
    902  /**This list describes the data paths in that order, which should be regard
    903   * calling {@link #setSample(float[])}.
    904   * return a new List which should not modify. Modify has no sense because the list is not used inside this class.
    905   */
    906  public List<GralSetValue_ifc> getTracks(){
    907    List<GralSetValue_ifc> ret = new LinkedList<GralSetValue_ifc>();
    908    List<Track> listTracks1 = listTracks; //Atomic access, iterate in local referenced list.
    909    for(Track track : listTracks1){
    910      ret.add(track);
    911    }
    912    return ret;
    913  }
    914
    915
    916  /**This Iterable describes the data paths in that order, which should be regard
    917   * calling {@link #setSample(float[])}.
    918   */
    919  public Iterable<? extends GralCurveViewTrack_ifc> getTrackInfo(){ return listTracks; }
    920
    921
    922
    923  /**Returns that track which was selected by set cursor at last.
    924   * @return null if a track was not selected up to now, elsewhere one of the created tracks.
    925   */
    926  public Track getTrackSelected(){ return _wdgImpl == null ? null : ((GraphicImplAccess)_wdgImpl).trackSelected; }
    927
    928
    929  public boolean shouldAutosave(){
    930    return saveOrg.ctValuesAutoSave >= saveOrg.nrofValuesAutoSave;
    931  }
    932
    933
    934  public long timeRight(){
    935    int ixData = ((GraphicImplAccess)super._wdgImpl).ixDataShown[0];  //right
    936    int timeShort1 = timeValues[(ixData >> shIxiData) & mIxiData];
    937    //synchronized()
    938    return timeorg.absTime.absTimeshort(timeShort1);
    939
    940  }
    941
    942
    943
    944  @Override public void setTimePoint(long date, int timeshort, float millisecPerTimeshort){
    945    timeorg.absTime.setTimePoint(date, timeshort, millisecPerTimeshort);
    946  }
    947
    948
    949  /**
    950   * @see org.vishia.gral.ifc.GralCurveView_ifc#setSample(float[], int)
    951   */
    952  @Override public void setSample(float[] values, int timeshort) {
    953    if(testStopWr) return;  //only for debug test.
    954    //if(++ixDataWr >= maxNrofXValues){ ixDataWr = 0; } //wrap arround.
    955    if( ++saveOrg.ctValuesAutoSave > saveOrg.nrofValuesAutoSave) {
    956      saveOrg.ctValuesAutoSave = saveOrg.nrofValuesAutoSave;  //no more.
    957    }
    958    ixDataWr += adIxData;  ///
    959    if(ixDataWr == -adIxData ){  //store to the last position in the data array
    960      dataOrg.bWrappedInBuffer = true;
    961    }
    962    if(!common.bFreeze && super._wdgImpl !=null){
    963      ((GraphicImplAccess)super._wdgImpl).ixDataShowRight = ixDataWr;
    964    }
    965    this.newSamples +=1;  //information for paint event
    966    this.nrofValuesForGrid +=1;
    967    if(nrofValuesForGrid > maxNrofXValues + gridDistanceStrongY){
    968      nrofValuesForGrid -= gridDistanceStrongY;  //prevent large overflow.
    969    }
    970    int ixSource = -1;
    971    int ixWr = (ixDataWr >> shIxiData) & mIxiData;
    972    //
    973    //
    974    //assign the argument values to the track.values
    975    //
    976    for(Track track: listTracks){
    977      if(ixSource < values.length -1){
    978        float val = values[++ixSource];  //write in the values.
    979        track.values[ixWr] = val;  //write in the values.
    980        if(track.min > val ){ track.min = val; }
    981        if(track.max < val ){ track.max = val; }
    982      } else {
    983        track.values[ixWr] = 0;
    984      }
    985    }
    986    //
    987    int timeLast = timeValues[ixWr];
    988    timeValues[ixWr] = timeshort;
    989
    990    timeorg.lastShortTimeDateInCurve = timeshort;
    991    if(nrofValues < maxNrofXValues){
    992      if(nrofValues ==0){
    993        timeorg.firstShortTimeDateInCurve = timeshort;
    994      }
    995      this.nrofValues +=1;
    996    } else {
    997      timeorg.firstShortTimeDateInCurve = timeLast;
    998      if(super._wdgImpl !=null) ((GraphicImplAccess)(super._wdgImpl)).nrofDataShift.incrementAndGet(); //shift data in graphic.
    999    }
    1000
    1001    if(!common.bFreeze){
    1002      synchronized(this){
    1003        if(super._wdgImpl !=null) ((GraphicImplAccess)(super._wdgImpl)).redrawBecauseNewData = true;
    1004      }
    1005      super.repaint(50,100);
    1006    }
    1007  }
    1008
    1009
    1010
    1011  /**
    1012   * @see org.vishia.gral.base.GralWidget#refreshFromVariable(org.vishia.byteData.VariableContainer_ifc)
    1013   */
    1014  @Override public void refreshFromVariable(VariableContainer_ifc container){
    1015    if(bActive){
    1016      List<Track> listTracks1 = listTracks; //Atomic access, iterate in local referenced list.
    1017      float[] values = new float[listTracks1.size()];
    1018      int ixTrack = -1;
    1019      boolean bRefreshed = false; //set to true if at least one variable is refreshed.
    1020      for(Track track: listTracks1){
    1021        if(track.variable ==null && bNewGetVariables){ //no variable known, get it.
    1022          String sDataPath = track.getDataPath();
    1023          if(sDataPath !=null){
    1024            if(sDataPath.startsWith("-")){ //it is a comment
    1025              int posEnd = sDataPath.indexOf('-',1) +1;
    1026              sDataPath = sDataPath.substring(posEnd);  //after second '-', inclusive first '-' if no second found.
    1027            }
    1028            String sPath2 = sDataPath.trim();
    1029            if(!sDataPath.startsWith("#")){ //don't regard commented line
    1030              String sPath = itsMng.getReplacerAlias().replaceDataPathPrefix(sPath2);  //replaces only the alias:
    1031              track.variable = container.getVariable(sPath);
    1032              if(track.variable == null){
    1033                System.err.printf("GralCurveView - variable not found; %s in curveview: %s\n", sPath, super.name);
    1034              }
    1035            }
    1036          }
    1037        }
    1038        final float value;
    1039
    1040        if(track.variable !=null ){
    1041          if(track.variable.isRefreshed()){
    1042            bRefreshed = true;
    1043          }
    1044          track.variable.requestValue();
    1045          if(track.getDataPath().startsWith("xxx:"))
    1046            stop();
    1047          value = track.variable.getFloat();
    1048          track.variable.requestValue(System.currentTimeMillis());
    1049        } else {
    1050          value = 0;
    1051        }
    1052        values[++ixTrack] = value;
    1053      }
    1054      bNewGetVariables = false;
    1055      final long timeyet = System.currentTimeMillis();
    1056      int timeshort;
    1057      if(this.common.timeDatapath !=null && this.common.timeVariable ==null){
    1058        String sPath = itsMng.getReplacerAlias().replaceDataPathPrefix(this.common.timeDatapath);  //replaces only the alias:
    1059        this.common.timeVariable = container.getVariable(sPath);
    1060      }
    1061      if(this.common.timeVariable !=null){
    1062        //the time variable should contain a relative time stamp. It is the short time.
    1063        //Usual it is in 1 ms-step. To use another step width, a mechanism in necessary.
    1064        //1 ms in 32 bit are ca. 2000000 seconds.
    1065        timeshort = this.common.timeVariable.getInt() + this.timeorg.timeshortAdd;
    1066        this.common.timeVariable.requestValue(timeyet);
    1067        if(this.timeorg.absTime.isCleaned()) {
    1068          setTimePoint(timeyet, timeshort, 1.0f);  //the first time set.
    1069        }
    1070        else if((timeshort - this.timeorg.timeshortLast) <0 || this.timeorg.absTime.isCleaned()) {
    1071          //new simulation time:
    1072          int timeshortAdd = this.timeorg.absTime.timeshort4abstime(timeyet);
    1073          timeshort += timeshortAdd;
    1074          this.timeorg.timeshortAdd += timeshortAdd;
    1075          setTimePoint(timeyet, timeshort, 1.0f);  //for later times set the timePoint newly to keep actual.
    1076        }
    1077      } else {
    1078        timeshort = (int)timeyet;  //The milliseconds from absolute time.
    1079        setTimePoint(timeyet, timeshort, 1.0f);  //set always a timePoint if not data time is given.
    1080      }
    1081      if(bRefreshed && timeshort != this.timeorg.timeshortLast) {
    1082        //don't write points with the same time, ignore seconds.
    1083        setSample(values, timeshort);
    1084        this.timeorg.timeshortLast = timeshort;
    1085      }
    1086    }
    1087  }
    1088
    1089
    1090
    1091  @Override
    1092  public GralColor setBackgroundColor(GralColor color)
    1093  {
    1094    // TODO Auto-generated method stub
    1095    return null;
    1096  }
    1097
    1098  @Override
    1099  public GralColor setForegroundColor(GralColor color)
    1100  {
    1101    // TODO Auto-generated method stub
    1102    return null;
    1103  }
    1104
    1105
    1106  @Override public void setBoundsPixel(int x, int y, int dx, int dy)
    1107  { //widgetSwt.setBounds(x,y,dx,dy);
    1108  }
    1109
    1110  @Override public void activate(boolean activate){ bActive = activate; }
    1111
    1112  @Override public boolean isActiv(){ return bActive; }
    1113
    1114  @Override public boolean isFreezed(){ return common.bFreeze; }
    1115
    1116
    1117
    1118
    1119
    1120  /* (non-Javadoc)
    1121   * @see org.vishia.gral.ifc.GralCurveView_ifc#applySettings(java.lang.String)
    1122   */
    1123  @Override public boolean applySettings(String in){
    1124    boolean bOk;
    1125    try{
    1126      //variate syntax in test...
    1127      Report console = new ReportWrapperLog(itsMng.log());
    1128      ZbnfParser parser = new ZbnfParser(console);
    1129      parser.setSyntax(syntaxSettings);
    1130      bOk = parser.parse(in);
    1131      if(!bOk){
    1132        console.writeError(parser.getSyntaxErrorReport());
    1133      } else {
    1134        this.common.timeDatapath = null;
    1135        this.common.timeVariable = null;
    1136        listTracks = new ArrayList<Track>();
    1137        //listTrackSet.clear();
    1138        ZbnfJavaOutput setData = new ZbnfJavaOutput(console);
    1139        setData.setContent(zbnfSetCurve.getClass(), zbnfSetCurve, parser.getFirstParseResult());
    1140        bNewGetVariables = true;  //to force searching variables.
    1141      }
    1142    } catch(Exception exc){
    1143      System.err.println("GralCurveView.writeSettings() - unexpected IOException;" + exc.getMessage());
    1144      bOk = false;
    1145    }
    1146    return bOk;
    1147  }
    1148
    1149
    1150
    1151  @Override public void writeSettings(Appendable out){
    1152    List<Track> listTracks1 = listTracks; //Atomic access, iterate in local referenced list.
    1153    for(Track track: listTracks1){
    1154      if(track.sDataPath !=null){
    1155        try{
    1156          out.append("track ").append(track.name).append(":");
    1157          out.append(" datapath=").append(track.sDataPath);
    1158          out.append(", color=").append(track.lineColor.toString());
    1159          out.append(", scale=").append(Float.toString(track.scale.yScale) );
    1160          out.append(", offset=").append(Float.toString(track.scale.yOffset));
    1161          out.append(", 0-line-percent=").append(Integer.toString(track.scale.y0Line));
    1162          out.append(";\n");
    1163        } catch(IOException exc){
    1164          System.err.println("GralCurveView.writeSettings() - unexpected IOException;" + exc.getMessage());
    1165        }
    1166      }
    1167    }
    1168  }
    1169
    1170
    1171  public CharSequence timeInitSaveViewArea(){
    1172    int ixData;
    1173    if(super._wdgImpl ==null) return "--no graphic--";
    1174    dataOrg.ixDataStartSave = ((GraphicImplAccess)super._wdgImpl).ixDataShown[dataOrg.zPixelDataShown-1];
    1175    int timeShort1 = timeValues[(dataOrg.ixDataStartSave >> shIxiData) & mIxiData];
    1176    dataOrg.ixDataEndSave = ((GraphicImplAccess)super._wdgImpl).ixDataShown[0];
    1177    if(!common.bFreeze){
    1178      //running curve, autosave starts after it.
    1179      dataOrg.ixDataEndAutoSave = dataOrg.ixDataEndSave;  //atomic access, the actual write pointer.
    1180      saveOrg.ctValuesAutoSave = 0;
    1181    }
    1182    return buildDate(dataOrg.ixDataStartSave, dataOrg.ixDataEndSave);
    1183    //return timeorg.absTime.absTimeshort(timeShort1);
    1184  }
    1185
    1186
    1187
    1188
    1189  @Override public CharSequence timeInitAutoSave(){
    1190    dataOrg.ixDataStartAutoSave = dataOrg.ixDataEndAutoSave;  //from last end, now start.
    1191    //threadsafe:
    1192    dataOrg.ixDataEndAutoSave = ixDataWr;  //atomic access, the actual write pointer.
    1193    saveOrg.ctValuesAutoSave = 0;  //maybe non-threadsafe, counts only the time for next save.
    1194    return buildDate(dataOrg.ixDataStartAutoSave, dataOrg.ixDataEndAutoSave);
    1195    //
    1196    //int timeShort1 = timeValues[(dataOrg.ixDataStartAutoSave >> shIxiData) & mIxiData];
    1197    //return timeorg.absTime.absTimeshort(timeShort1);  //The appropriate start time.
    1198
    1199  }
    1200
    1201
    1202
    1203
    1204  private CharSequence buildDate(int ixStart, int ixEnd){
    1205    int timeShortStart = timeValues[(ixStart >> shIxiData) & mIxiData];
    1206    int timeShortEnd = timeValues[(ixEnd >> shIxiData) & mIxiData];
    1207    long timeStart = timeorg.absTime.absTimeshort(timeShortStart);
    1208    long timeEnd = timeorg.absTime.absTimeshort(timeShortEnd);
    1209    DateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
    1210    DateFormat formatEnd = new SimpleDateFormat("_HH-mm-ss");
    1211    String sNameFile = format.format(new Date(timeStart)) + "_to" + formatEnd.format(new Date(timeEnd));
    1212    return sNameFile;
    1213  }
    1214
    1215
    1216
    1217
    1218
    1219  /**Reads a curve ///
    1220   *
    1221   */
    1222  public void readCurve(File file)
    1223  throws IOException
    1224  {
    1225    InputStream ifile = new FileInputStream(file);
    1226    //read first bytes to detect format
    1227    byte[] buffer = new byte[256];
    1228    int zBytes = ifile.read(buffer);
    1229    ifile.close();
    1230    String firstLine = new String(buffer,0, zBytes);
    1231    readCurveCsvHeadline(file);
    1232    //timeLastSave = System.currentTimeMillis();
    1233
    1234  }
    1235
    1236
    1237
    1238  private void readCurveCsvHeadline(File file)
    1239  throws IOException
    1240  {
    1241    String sLine;
    1242    BufferedReader ifile = new BufferedReader(new FileReader(file));
    1243    //first head line
    1244    setTimePoint(System.currentTimeMillis(), 0, 0.156f);
    1245    //line with channels
    1246    sLine = ifile.readLine();
    1247    //List<Track> listTracksNew = new ArrayList<Track>();  ////
    1248    //listTrackSet.clear();
    1249    int ixInList = -1;
    1250    String[] signals = sLine.split(";");
    1251    int[] dataIx = new int[signals.length +10];
    1252    String[] colors = {"bk","rd","gn","bl","or","rd1","rd2","gn1","gn2","bk","rd","gn","bl","or","rd1","rd2","gn1"};
    1253    int ix = -1;
    1254    int ixColor = 0;
    1255    boolean first_timesh = false;
    1256    for(String signal1: signals){
    1257      String signal = signal1.trim();
    1258      if(signal.equals("timesh") && ix == -1) {
    1259        first_timesh = true;
    1260      } else {
    1261        Track track = searchTrack(signal);
    1262        if(track !=null) {
    1263          //listTracksNew.add(track);
    1264        } else {
    1265          GralColor color = GralColor.getColor(colors[ixColor]);
    1266          track = addTrack(signal, signal, color, 1, 50, 1.0f, 0.0f); //, listTracksNew);
    1267          if(ixColor < colors.length-1) {ixColor +=1;}
    1268        }
    1269        dataIx[++ix] = track.ixList; //getDataIx();
    1270      }
    1271    }
    1272    //listTracks = listTracksNew;
    1273    this.ixDataWr = -adIxData; //initial write position, first increment to 0.
    1274    float[] fvalues = new float[listTracks.size() + 10];
    1275    int timeshort = 0;
    1276    while( (sLine = ifile.readLine())!=null){
    1277      timeshort +=1;
    1278      String[] values = sLine.split(";");
    1279      int ixCol = -1;
    1280      boolean bFirstCol = true;
    1281      for(String value1: values){
    1282        String sValue = value1.trim();
    1283        sValue = sValue.replace(',', '.');  //accept , as fractional separator, convert to .
    1284        if(first_timesh && bFirstCol) {
    1285          try{ timeshort = Integer.valueOf(sValue); }
    1286          catch(NumberFormatException exc) {
    1287            timeshort +=1;  //use incremented
    1288          }
    1289        } else {
    1290          float value;
    1291          try{ value = Float.valueOf(sValue); }
    1292          catch(NumberFormatException exc){
    1293            value = 0.099999999f;
    1294          }
    1295          int ixData = dataIx[++ixCol];
    1296          fvalues[ixData] = value;
    1297        }
    1298        bFirstCol = false;
    1299      }
    1300      setSample(fvalues, timeshort);
    1301    }
    1302    ifile.close();
    1303  }
    1304
    1305
    1306
    1307
    1308
    1309  /* (non-Javadoc) Writes the curve to the given interface, it is an exporter class.
    1310   * @see org.vishia.gral.ifc.GralCurveView_ifc#writeCurve(org.vishia.curves.WriteCurve_ifc, org.vishia.gral.ifc.GralCurveView_ifc.ModeWrite)
    1311   */
    1312  @Override public void writeCurve(WriteCurve_ifc out, ModeWrite mode){
    1313    //int ctValues = this.nrofValues -1;  //read first, may be increment in next step
    1314    int ixDataStart, ixDataEnd;
    1315    switch(mode){
    1316      case currentView:{
    1317        ixDataEnd = dataOrg.ixDataEndSave;
    1318        ixDataStart = dataOrg.ixDataStartSave;
    1319        /*
    1320        int xRight = 0; //xpCursor2
    1321        //This operation should be done in the graphic thread because ixDataShown is volatile.
    1322        int xLeft = dataOrg.xPixelCurve-1;  //xpCursor1
    1323        if(xLeft >=0 && xLeft < ixDataShown.length){
    1324          ixDataStart = ixDataShown[xLeft];
    1325        } else {
    1326          ixDataStart = ixDataWr - (ctValues << shIxiData);  //read only one time, the index start from.
    1327        }
    1328        if(xRight >=0 && xRight < ixDataShown.length){
    1329          ixDataEnd = ixDataShown[xRight];
    1330        } else {
    1331          ixDataEnd = ixDataWr;   //never used.
    1332        }
    1333        */
    1334      } break;
    1335      case autoSave: {  //NOTE: values are set in timeInitAutoSave()
    1336        ixDataEnd = dataOrg.ixDataEndAutoSave;
    1337        ixDataStart = dataOrg.ixDataStartAutoSave;
    1338      }break;
    1339      default:
    1340        ixDataStart = ixDataEnd = 0;
    1341    }
    1342    //todo in another thread!
    1343    writeCurve(out, ixDataStart, ixDataEnd);
    1344  }
    1345
    1346
    1347
    1348
    1349
    1350
    1351  /**Writes.
    1352   * @param out
    1353   * @param ixDataStart 32-bit-wrapping index
    1354   * @param ixDataEnd
    1355   */
    1356  private void writeCurve(WriteCurve_ifc out, int ixDataStart, int ixDataEnd)
    1357  //throws IOException
    1358  { boolean bOk = true;
    1359    try{
    1360      out.writeCurveTimestamp(new Timeshort(timeorg.absTime));
    1361      if(!bOk){
    1362        out.writeCurveError("absolute time error");
    1363      } else {
    1364        int ixTrack = -1;
    1365        List<Track> listTracks1 = listTracks; //Atomic access, iterate in local referenced list.
    1366        int nrofTracks = listTracks1.size();
    1367        for(Track track: listTracks1){
    1368          String sName = track.name;
    1369          String sPath = track.getDataPath();
    1370          GralColor color = track.getLineColor();
    1371          String sColor = color.getColorName();
    1372          out.setTrackInfo(nrofTracks, ++ixTrack, sPath, sName, sColor, track.getScale7div(), track.getOffset(), track.getLinePercent());
    1373        }
    1374        float[] record = new float[listTracks1.size()];
    1375        int ix = (ixDataStart >> shIxiData) & mIxiData;
    1376        int timeshortLast = timeValues[ix];
    1377        out.writeCurveStart(timeshortLast);
    1378        int ixData = ixDataStart;
    1379        int ctValues = this.nrofValues -1;  //read first, may be increment in next step
    1380        while((ixData != ixDataEnd && --ctValues >=0)){
    1381          ix = (ixData >> shIxiData) & mIxiData;
    1382          ixTrack = -1;
    1383          for(Track track: listTracks1){
    1384            record[++ixTrack] = track.values[ix];
    1385          }
    1386          int timeshort = timeValues[ix];
    1387          if((timeshort - timeshortLast)<0){
    1388            //This is a older value since the last one,
    1389            //it means it is the first value, all others are overwritten.
    1390            out.writeCurveStart(timeshort);
    1391          }
    1392          out.writeCurveRecord(timeshort, record);
    1393          timeshortLast = timeshort;
    1394          ixData += adIxData;
    1395        }
    1396        out.writeCurveFinish();
    1397      }
    1398    }catch(IOException exc){
    1399      System.err.println(Assert.exceptionInfo("GralCurveView- exception", exc, 0, 4));
    1400    }
    1401  }
    1402
    1403
    1404  /**Forces repaint of the whole curves. It sets the internal flag {@link #bPaintAllCmd} which is checked
    1405   * in the paint event routine. In opposite {@link #setSample(float[], int)} invokes
    1406   * super.repaint of {@link GralWidget#repaint(int, int)} without setting that flag. Therefore the graphic
    1407   * will be shifted to left only with paint of only the new data.
    1408   * @see org.vishia.gral.base.GralWidget#repaint(int, int)
    1409   */
    1410  @Override public void repaint(int delay, int latest){
    1411    System.out.println("GralCurveView.Info - repaint all Trigger;");
    1412    if(_wdgImpl == null) return;
    1413    ((GraphicImplAccess)_wdgImpl).bPaintAllCmd = true;      //used in implementation level to force a paint of the whole curves.
    1414    bNewGetVariables= true;   //used to get faulty variables newly with an error message.
    1415    super.repaint(delay, latest);
    1416  }
    1417
    1418
    1419
    1420
    1421
    1422
    1423
    1424  /**All tracks to return for filling. It has the same members in the same order like {@link #listTracks}*/
    1425  //protected final List<GralSetValue_ifc> listTrackSet = new LinkedList<GralSetValue_ifc>();
    1426
    1427
    1428  public abstract static class GraphicImplAccess extends GralWidget.ImplAccess
    1429  implements GralWidgImpl_ifc, Removeable
    1430  {
    1431    protected final GralCurveView widgg;
    1432
    1433    protected final PixelOrganisation pixelOrg = new PixelOrganisation();
    1434
    1435    /**The track which is selected by the last setCursor. */
    1436    protected Track trackSelected;
    1437
    1438    /**The number of pixel for the current data point from the current pixel to left.
    1439     * [0] is the number of pixel for the right point.
    1440     * This array is parallel to {@link #ixDataShown}.
    1441     */
    1442    protected final int[] nrofPixel4data = new int[2000];
    1443
    1444    /**The index in data for each shown pixel, from right to left int-wrapping.
    1445     * [0] is right. Use <code>(ixData >> shIxiData) & mIxiData</code> to calculate the real data index.
    1446     * the used length is the number of pixel. 2000 are enough for a large representation.
    1447     * This array is filled newly whenever any draw or paint action is done. It is prepared in the routine
    1448     * The field contains old indices if the size of drawing is less then the size of window.
    1449     * {@link #prepareIndicesDataForDrawing(int, int, int)} and used in the drawTrack routine of the implementation level.
    1450     */
    1451    protected final int[] ixDataShown = new int[2000];
    1452
    1453    /**The index to show values, it increments with ixWrValues
    1454     * if bFreeze is false
    1455     */
    1456    protected int ixDataShowRight = 0;
    1457
    1458    //protected int ixLineInit = 0;
    1459
    1460    //protected final float[][] values;
    1461
    1462    /**last x-positions for paint.
    1463     * The lastPositionX[0] is used to repaint the last curve peace while shifting draw content.
    1464     * Where, xShift (number of shifted pixel) is considered by subtraction.
    1465     */
    1466    protected final int[] lastPositionX = new int[2];
    1467
    1468    /**Number of values to show in graphic. */
    1469    protected int XXXnrofValuesShow;
    1470
    1471    /**The cary over of time which is not used for the current point.
    1472     *
    1473     */
    1474    protected int timeCaryOverNewValue;
    1475
    1476    /**Index of the last drawn values.
    1477     * The index refers to the last drawn value. */
    1478    protected int ixDataDraw = 0;
    1479
    1480    /**The actual number of values which are not shown because its time difference is too small
    1481     * to show in graphic as new point. It is counted only for debugging.
    1482     */
    1483    protected int nrofValuesLessViewPart;
    1484
    1485    /**Pixel from right for the cursor1 and cursor2. If -1 then the cursor is unused yet.
    1486     */
    1487    protected int xpCursor1 = -1;
    1488
    1489    /**Pixel from right for the cursor1 and cursor2. If -1 then the cursor is unused yet.
    1490     */
    1491    protected int xpCursor2 = -1;
    1492
    1493    /**New Pixel position for a cursor1 and cursor2. If >=0 then a new position is given.
    1494     */
    1495    protected int xpCursor1New = -1;
    1496
    1497    /**New Pixel position for a cursor1 and cursor2. If >=0 then a new position is given.
    1498     */
    1499    protected int xpCursor2New = -1;
    1500
    1501    /**During mouse move, internal use. */
    1502    protected boolean bMouseDownCursor1;
    1503
    1504    /**During mouse move, internal use. */
    1505    protected boolean bMouseDownCursor2;
    1506
    1507    protected final static int cmdSetCursor = -2;
    1508
    1509    /**Position of cursor in the data. */
    1510    protected int ixDataCursor1;
    1511
    1512    /**Position of cursor in the data. */
    1513    protected int ixDataCursor2;
    1514
    1515    /**Distance of nrofValues for one vertical grid line (strong or not strong). */
    1516    protected int gridDistanceY;
    1517
    1518    /**Distance of percent of y-view for one vertical grid line (strong or not strong). */
    1519    protected float gridDistanceX;
    1520
    1521    /**period of strong lines. */
    1522    protected int gridStrongPeriodX;
    1523
    1524    /**period of strong lines. */
    1525    protected int gridStrongPeriodY;
    1526
    1527    protected GralColor gridColorGral; // = new Color(getDisplay(), 0, 255, 255);
    1528
    1529    protected GralColor gridColorGralStrong; // = new Color(getDisplay(), 0, 255, 255);
    1530
    1531    /**Zoom factor. If zoom = 1.0, the curve will be shown in the current canvas area.
    1532     * It the zoom-factor is greater 1.0, not all points will be shown.
    1533     * The origin point of the values is given with {@link #xOrigin} and {@link #yOrigin}.
    1534     */
    1535    //protected float xZoom = 1.0F, yZoom= 1.0F;
    1536
    1537    /**Origin point from 0.0 to 1.0 for the zoomed area. */
    1538    //protected float xOrigin = 0.0F, yOrigin = 0.0F;
    1539
    1540    protected GralColor colorBackGral; // = new Color(getDisplay(), 0xff, 0xff, 0xff);
    1541
    1542    protected boolean focusChanged = false;  //it doesn't work
    1543
    1544    /**last point in x where values were drawn. */
    1545    protected float xViewLastF = 0;
    1546
    1547    /**Number of iData-indices, which are shifted in the {@link #values}.
    1548     * This number have to be shifted in the pixel area if a draw-all is not requested.
    1549     * This field is set to 0 if the draw action is done.*/
    1550    protected AtomicInteger nrofDataShift = new AtomicInteger(0);
    1551
    1552    protected float nrofDataShiftFracPart = 0.0F;
    1553
    1554    /**Set true if {@link #redrawData()} is called. Then only the area for new data is drawn
    1555     * in the {@link #drawBackground(GC, int, int, int, int)}-routine.
    1556     *
    1557     */
    1558    protected boolean redrawBecauseNewData;
    1559
    1560    protected boolean bRedrawAll;
    1561
    1562    private int ctLastSelected;
    1563
    1564    /**Set to true to force a paint all. */
    1565    protected boolean bPaintAllCmd = false;
    1566
    1567    protected GraphicImplAccess(GralCurveView outer){
    1568      super(outer);
    1569      widgg = outer;
    1570      this.pixelOrg.xPixelCurve = 0;
    1571      this.pixelOrg.yPixelCurve = 0;
    1572
    1573    }
    1574
    1575    /**Forces that the next repaint paints the whole graphic.
    1576     * This method should be invoked if the conditions for the graphic are changed, for example
    1577     * changed colors for lines.
    1578     */
    1579    protected void setPaintAllCmd(){
    1580      System.out.println("GralCurveView.Info - paintallTrigger;");
    1581      bPaintAllCmd = true;      //used in implementation level to force a paint of the whole curves.
    1582      widgg.bNewGetVariables= true;   //used to get faulty variables newly with an error message.
    1583    }
    1584
    1585    /**Gets the index in data with given pixel position in the graphic.
    1586     * The wrapping index in the data is contained in {@link #ixDataShown}.
    1587     * @param ixw pixel position countered from right side.
    1588     * @return
    1589     */
    1590    protected int getIxDataFromPixelRight(int ixPixelFromRight){
    1591      int ixDataWrap = ixDataShown[ixPixelFromRight];
    1592      int ixData = (ixDataWrap >> widgg.shIxiData) & widgg.mIxiData;
    1593      return ixData;
    1594    }
    1595
    1596    /**Determines and switches a curve to select by mouse click.
    1597     * The curve should be at least 10 pixel near the mouse position.
    1598     * <br>
    1599     * Algorithm: From mouse position the index in the data are calculated: {@link #getIxDataFromPixelRight(int)}.
    1600     * Any track  is checked: The value (float) is read, the scaling is used to calculate the y-Position.
    1601     * Then the distance between mouse and track is calculated.
    1602     *
    1603     * @param xpos Mouse position on click in pixel
    1604     * @param ypos
    1605     * @param xsize size of the curve view widget.
    1606     * @param ysize
    1607     * @return true if a track is selected. false if the position is more as 10 pixels far of any track.
    1608     */
    1609    protected boolean selectTrack(int xpos, int ypos, int xsize, int ysize){
    1610      int ixData = getIxDataFromPixelRight(xsize - xpos);  //index countered from right to left
    1611      int minFound = 10;           //at least 10 pixel near found curve.
    1612      int maxDiffLastSelected = 0;
    1613      Track foundTrack = null;
    1614      ctLastSelected +=1;
    1615      List<Track> listTracks1 = widgg.listTracks; //Atomic access, iterate in local referenced list.
    1616      for(Track track: listTracks1){  //NOTE: break inside.
    1617        float val = track.values[ixData];
    1618        float yFactor = ysize / -10.0F / track.scale.yScale;  //y-scaling
    1619        float y0Pix = (1.0F - track.scale.y0Line/100.0F) * ysize; //y0-line
    1620
    1621        int yp = (int)((val - track.scale.yOffset) * yFactor + y0Pix);
    1622        int diff = Math.abs(yp - ypos);
    1623        int diffLastSelected = ctLastSelected - track.identLastSelect;
    1624        if(diff < minFound && diffLastSelected > maxDiffLastSelected){
    1625          foundTrack = track;
    1626          maxDiffLastSelected = diffLastSelected;
    1627          //minFound = diff;
    1628        }
    1629      }
    1630      if(foundTrack !=null){
    1631        foundTrack.identLastSelect = ctLastSelected;
    1632        trackSelected = foundTrack;
    1633        if(widgg.actionSelectTrack !=null){
    1634          widgg.actionSelectTrack.exec(0, widgg, trackSelected);
    1635        }
    1636      }
    1637      return foundTrack !=null;
    1638    }
    1639
    1640    protected void setCursors(int xPos){
    1641      //System.out.println("middle");
    1642      int xr = pixelOrg.xPixelCurve - xPos;  //from right
    1643      if(xpCursor1 < 0){  //the right cursor
    1644        xpCursor1New = xr;
    1645        bMouseDownCursor1 = true;
    1646        bMouseDownCursor2 = false;
    1647      } else if(xpCursor2 < 0){
    1648        xpCursor2New = xr;
    1649        bMouseDownCursor2 = true;
    1650        bMouseDownCursor1 = false;
    1651      } else { //decide which cursor
    1652        //use the new changed value if not processed in graphic yet or the current cursor.
    1653        int x1 = xpCursor1New >=0 ? xpCursor1New: xpCursor1;
    1654        int x2 = xpCursor2New >=0 ? xpCursor2New: xpCursor2;
    1655        if(x1 < x2){
    1656          int xp = x1;      //swap
    1657          xpCursor1New = x2;
    1658          xpCursor2New = xp;
    1659          //System.out.printf("GralCurveView - setCursors - swap; xC1=%d; xC2=%d\n", x2, xp);
    1660        }
    1661        int xm = (x1 + x2) /2;
    1662        if(xr > xm){ //more left
    1663          xpCursor1New = xr;
    1664          bMouseDownCursor1 = true;
    1665          bMouseDownCursor2 = false;
    1666          //System.out.printf("GralCurveView - setCursors; cursor1=%d\n", xr);
    1667        } else {
    1668          xpCursor2New = xr;
    1669          bMouseDownCursor2 = true;
    1670          bMouseDownCursor1 = false;
    1671          //System.out.printf("GralCurveView - setCursors; cursor2=%d\n", xr);
    1672        }
    1673      }
    1674      bRedrawAll = true;
    1675      widgg.repaint(0,0);
    1676      //repaint(50,100);
    1677      if(widgg.actionMoveCursor !=null){
    1678        widgg.actionMoveCursor.exec(0, widgg);
    1679      }
    1680    }
    1681
    1682    protected void moveCursor(int xMousePixel){
    1683      int xr = pixelOrg.xPixelCurve - xMousePixel;  //from right
    1684      if(bMouseDownCursor1){
    1685        xpCursor1New = xr;  //from right;
    1686        //System.out.println("SwtCurveView.mouseMove - cursor1; xr=" + xr);
    1687        widgg.repaint(50,100);
    1688      } else if(bMouseDownCursor2){
    1689        xpCursor2New = xr;  //from right;
    1690        //System.out.println("SwtCurveView.mouseMove - cursor2; xr=" + xr);
    1691        widgg.repaint(50,100);
    1692      } else {
    1693        //System.out.println("SwtCurveView.mouseMove x,y=" + e.x + ", " + e.y);
    1694
    1695      }
    1696      if(widgg.actionMoveCursor !=null){
    1697        widgg.actionMoveCursor.exec(0, widgg);
    1698      }
    1699    }
    1700
    1701    /**Zooms the curve presentation with same index right with a lesser time spread.
    1702     * If the curve presentation is running yet, a finer solution in the present is given.
    1703     * Note that in this case the right index is the actual write index.*/
    1704    protected void zoomToPresent(){
    1705      if(xpCursor1 >=0){
    1706        ixDataCursor1 = ixDataShown[xpCursor1];
    1707      }
    1708      if(xpCursor2 >=0){
    1709        ixDataCursor2 = ixDataShown[xpCursor2];
    1710      }
    1711      xpCursor1New = xpCursor2New = cmdSetCursor;
    1712      if(widgg.timeorg.timeSpread > 100) { widgg.timeorg.timeSpread /=2; }
    1713      else { widgg.timeorg.timeSpread = 100; }
    1714      bPaintAllCmd = true;
    1715      widgg.repaint(100, 200);
    1716    }
    1717
    1718    /**Zooms the curve presentation with same index right with a greater time spread.
    1719     * If the curve presentation is running yet, a broader solution in the present is given.
    1720     * Note that in this case the right index is the actual write index.*/
    1721    protected void zoomToPast(){
    1722      //zoom out
    1723      int maxTimeSpread = widgg.timeorg.lastShortTimeDateInCurve - widgg.timeorg.firstShortTimeDateInCurve;
    1724      if(xpCursor1 >=0){
    1725        ixDataCursor1 = ixDataShown[xpCursor1];
    1726      }
    1727      if(xpCursor2 >=0){
    1728        ixDataCursor2 = ixDataShown[xpCursor2];
    1729      }
    1730      xpCursor1New = xpCursor2New = cmdSetCursor;
    1731      if(widgg.timeorg.timeSpread < 0x3fffffff) { widgg.timeorg.timeSpread *=2; }
    1732      else { widgg.timeorg.timeSpread = 0x7fffffff; }
    1733      bPaintAllCmd = true;
    1734      widgg.repaint(100, 200);
    1735
    1736    }
    1737
    1738    /**Zooms between the given vertical cursors.
    1739     * The time spread is calculated so that the cursors are places on the same data indices at
    1740     * 1/10 and 9/10 of the presentation range.
    1741     */
    1742    protected void zoomBetweenCursors(){
    1743      ixDataCursor1 = ixDataShown[xpCursor1];
    1744      ixDataCursor2 = ixDataShown[xpCursor2];
    1745      ixDataShowRight = ixDataCursor2 + (((ixDataCursor2 - ixDataCursor1) / 10) & widgg.mIxData);
    1746      int ixiData1 = (ixDataShown[xpCursor1] >> widgg.shIxiData) & widgg.mIxiData;
    1747      int ixiData2 = (ixDataShown[xpCursor2] >> widgg.shIxiData) & widgg.mIxiData;
    1748      int time1 = widgg.timeValues[ixiData1];
    1749      int time2 = widgg.timeValues[ixiData2];
    1750      if((time2 - time1)>0){
    1751        widgg.timeorg.timeSpread = (time2 - time1) * 10/8;
    1752        assert(widgg.timeorg.timeSpread >0);
    1753      } else {
    1754        widgg.stop();
    1755      }
    1756      xpCursor1New = xpCursor2New = cmdSetCursor;
    1757      widgg.repaint(100, 200);
    1758
    1759    }
    1760
    1761    /**Unzooms between the given vertical cursors.
    1762     * The cursors will be set newly in graphic at the same data indices.
    1763     * The middle value of both cursors will be at the same position.
    1764     * The time spread is multiplied by the value 5.
    1765     * If the right border of presentation will be in the future, the {@link #ixDataShowRight}
    1766     * will be greater than {@link #ixDataWr}, then the presentation will be adjusted to the current
    1767     * right {@link #ixDataWr} and the cursor positions are changed adequate.
    1768     *
    1769     */
    1770    protected void cursorUnzoom()
    1771    {
    1772      int xpCursorMid = (xpCursor1 + xpCursor2) /2;
    1773      int ixDataMid = ixDataShown[xpCursorMid];  //from Pixel value of cursor to data index
    1774      //int timeMid = timeValues[(ixDataMid >> shIxiData) & mIxiData];
    1775      //int timeRight = timeValues[(ixDataShowRight >> shIxiData) & mIxiData];
    1776      //int dtime = timeRight - timeMid;
    1777      //Assert.check(dtime > 0);
    1778      ixDataShowRight = ixDataMid + (ixDataShowRight - ixDataMid) * 5;  //5 times longer
    1779      if((ixDataShowRight - widgg.ixDataWr) >0){
    1780        ixDataShowRight = widgg.ixDataWr;    //show full to right
    1781      }
    1782      //ixDataCursor1 = ixDataShown[xpCursor1];  //from Pixel value of cursor to data index
    1783      //ixDataCursor2 = ixDataShown[xpCursor2];
    1784      //ixDataShowRight = ixDataCursor2 + (((ixDataCursor2 - ixDataCursor1)));
    1785      if(widgg.timeorg.timeSpread < (0x7ffffff5/5)) { widgg.timeorg.timeSpread *=5; }
    1786      else { widgg.timeorg.timeSpread = 0x7fffffff; }
    1787      /*
    1788      int ixiData2 = (ixDataCursor2 >> shIxiData) & mIxiData;
    1789      int time1 = timeValues[(ixDataCursor1 >> shIxiData) & mIxiData];
    1790      int time2 = timeValues[ixiData2];
    1791      timeSpread = (time2 - time1) * 5;
    1792      */
    1793      Assert.check(widgg.timeorg.timeSpread >0);
    1794      xpCursor1New = xpCursor2New = cmdSetCursor;
    1795      widgg.repaint(100, 200);
    1796    }
    1797
    1798    /**Shifts the curve presentation to the present (actual values).
    1799     * If the TODO
    1800     */
    1801    protected void viewToPresentOrGoIrRefresh()
    1802    {
    1803      if(widgg.common.bFreeze){
    1804        //assume that the same time is used for actual shown data spread as need
    1805        //for the future.
    1806
    1807        //int timeRight = timeValues[(ixDataShowRight >> shIxiData) & mIxiData];
    1808        //int timeRightNew = timeRight + timeorg.timeSpread * 7/8;
    1809
    1810        int ixdDataSpread = ixDataShowRight - ixDataShown[pixelOrg.xPixelCurve * 5/8];
    1811        if((ixDataShowRight - widgg.ixDataWr)<0 && (ixDataShowRight - widgg.ixDataWr + ixdDataSpread) >=0){
    1812          //right end reached.
    1813          ixDataShowRight = widgg.ixDataWr;
    1814          widgg.common.bFreeze = false;
    1815        } else {
    1816          ixDataShowRight += ixdDataSpread;
    1817        }
    1818        //ixDataShowRight += ixdDataSpread;
    1819        //if((ixDataShowRight - ixDataWr) > 0 && (ixDataShowRight - ixDataWr) < ixdDataSpread * 2) {
    1820          //right end reached.
    1821          //ixDataShowRight = ixDataWr;
    1822          //common.bFreeze = false;
    1823          //ixDataShowRight1 = ixDataWr + ixdDataSpread;
    1824        //}
    1825        //ixDataShowRight += ixDataShown[0] - ixDataShown[nrofValuesShow-1];
    1826        widgg.repaint(100, 200);
    1827
    1828      } else {
    1829        setPaintAllCmd();  //refresh
    1830      }
    1831      //System.out.println("right-bottom");
    1832    }
    1833
    1834    /**Shifts the curve presentation to the past. If the presentation is running, it is stopped firstly.
    1835     * If it is stopped, the data index on 20% of current presentation will be adjusted to the right border.
    1836     * It means 20% are presented overlapping. The time spread is not changed.
    1837     */
    1838    protected void stopAndViewToPast()
    1839    {
    1840      if(!widgg.common.bFreeze){
    1841        widgg.common.bFreeze = true;
    1842        //now ixDataShow remain unchanged.
    1843      } else {
    1844        int ixdDataSpread = ixDataShowRight - ixDataShown[pixelOrg.xPixelCurve * 5/10];
    1845        //int ixDataShowRight1 = ixDataShown[nrofValuesShow * 7/8];
    1846        //int ixdDataSpread = ixDataShowRight - ixDataShowRight1;
    1847        ixDataShowRight -= ixdDataSpread;
    1848        if((ixDataShowRight - widgg.ixDataWr) < 0 && (widgg.ixDataWr - ixDataShowRight) < ixdDataSpread) {
    1849          //left end reached.
    1850          ixDataShowRight = widgg.ixDataWr + ixdDataSpread;
    1851        }
    1852      }
    1853      widgg.repaint(100, 200);
    1854      //System.out.println("left-bottom");
    1855
    1856    }
    1857
    1858    protected void mouseSelectCursur(int xMousePixel, int yMousePixel){
    1859
    1860    }
    1861
    1862    /**prepares indices of data.
    1863     * All data have a timestamp. the xpixel-values are calculated from the timestamp.
    1864     * The {@link #ixDataShown} array will be filled with the indices to the data for each x pixel from right to left.
    1865     * If there are more as one data record for one pixel, the step width of ixData is >1,
    1866     * If there are more as one x-pixel for one data record, the same index is written for that pixel
    1867     * <br><br>
    1868     * It uses the {@link timeValues} to get the timestamp of the values starting from param ixDataRight
    1869     * for the right pixel of view. The ixDataRight will be decremented. It is a wrapping index with
    1870     * step width of {@link #adIxData}. If the timestamp of any next index is greater than the last one,
    1871     * it is a indication that any newer data are reached after wrapping. Then the preparation stops.
    1872     * <br><br>
    1873     * It fills {@link #ixDataShown} with indices to data per x pixel point.
    1874     * If there are the same data for more as one pixel (zoomed near), then {@link #ixDataShown} will contain
    1875     * the same index. The presentation can decide whether it should be shown with the same level (stepwise)
    1876     * or with any linear approximation
    1877     *
    1878     * @param ixDataRight Index in data array for the right point.
    1879     *   It is a index which wraps around full integer range, see {@link #adIxData}.
    1880     * @param xViewPart width of the spread to prepare in pixel
    1881     * @return nrof pixel to draw. It is xViewPart if enough data are available, elsewhere less.
    1882     * TODO move to ImplAccess
    1883     */
    1884    protected int prepareIndicesDataForDrawing(int ixDataRight, int xViewPart, int timePart, boolean bPaintAll){
    1885      System.arraycopy(ixDataShown, 0, ixDataShown, xViewPart, ixDataShown.length - xViewPart);
    1886      if(bPaintAll){
    1887        widgg.dataOrg.zPixelDataShown = 0;
    1888      } else if(widgg.dataOrg.zPixelDataShown + xViewPart >= pixelOrg.xPixelCurve){
    1889        widgg.dataOrg.zPixelDataShown = pixelOrg.xPixelCurve;  // the whole curve fills.
    1890      } else {
    1891        widgg.dataOrg.zPixelDataShown += xViewPart;
    1892      }
    1893      //
    1894      //    time,   time2;   ......timeRight     the time stamps in data
    1895      //    ixData, ixData2, ..... ixDataRight   the 32-bit-index to data
    1896      //    ixD                              the really index to data
    1897      //
    1898      ixDataShown[0] = ixDataRight;
    1899      //System.out.println("GralCurveView - prepareIndices; timePart=" + timePart + "; timexPart=" + xViewPart * timePerPixel + "; xPart=" + xViewPart);
    1900      int ixData = ixDataRight;
    1901      int ixData2 = ixDataRight;
    1902      int ixD = (ixData >> widgg.shIxiData) & widgg.mIxiData;
    1903      int ixp2 = 0;
    1904      int ixp = 0; //pixel from right to left
    1905      int nrofPixel4Data =0;
    1906      final int timeRight = widgg.timeValues[ixD]; //timestamp of the right value.
    1907      //
    1908      if(xViewPart > 100){
    1909        widgg.timeorg.timeLeftShowing = timeRight - (int)((xViewPart +1) * widgg.timeorg.timePerPixel);
    1910      }
    1911      //calculate absolute time from shorttime:
    1912      long millisecAbs = widgg.timeorg.absTime.absTimeshort(timeRight);
    1913      int milliSec2Div = (int)(millisecAbs % widgg.timeorg.millisecPerDiv);                                 //how many millisec to the next division
    1914      int milliSec2FineDiv = milliSec2Div % ( widgg.timeorg.millisecPerFineDiv);
    1915      float pixel2FineDiv = milliSec2FineDiv * widgg.timeorg.pixel7time / widgg.timeorg.absTime.millisec7short(); //how many pixel to the next fine division line
    1916      float pixel2Div = milliSec2Div * widgg.timeorg.pixel7time / widgg.timeorg.absTime.millisec7short();         //how many pixel to the next division line
    1917      int ixPixelTimeDiv =-1;
    1918      int ixPixelTimeDivFine =-1;  //sets widgg.timeorg.sTimeAbsDiv[...], widgg.timeorg.xPixelTimeDivFine[...] TODO clean arrays before, better for debugging or set to -1 for stop point, see affter while-loop
    1919      while(pixel2FineDiv < xViewPart ){ //&& nrofPixel4Data >=0){
    1920        if(Math.abs(pixel2Div - pixel2FineDiv) < 3){
    1921          //strong division
    1922          int xPixel = (int)(pixel2Div + 1.5); //  (xTimeDiv * widgg.timeorg.pixel7time + 0.5f) ;
    1923          widgg.timeorg.xPixelTimeDiv[++ixPixelTimeDiv] = xPixel;
    1924          widgg.timeorg.timeAbsOnLastStrongDiv = millisecAbs - milliSec2Div; //(long)(timeRight - xTimeDiv - widgg.timeorg.absTime_short + widgg.timeorg.absTime);
    1925          if( widgg.timeorg.millisecPerFineDiv <= 100 ){ //less 1 sec for strong division:
    1926            float millisec = (( widgg.timeorg.timeAbsOnLastStrongDiv) % 60000) / 1000.0f;
    1927            widgg.timeorg.sTimeAbsDiv[ixPixelTimeDiv] = String.format("% 2.3f", millisec);
    1928
    1929          } else if( widgg.timeorg.millisecPerFineDiv < 1000){  //less 10 sec for strong division, but more 1 sec
    1930            long seconds = (( widgg.timeorg.timeAbsOnLastStrongDiv / 1000) % 60);
    1931            //System.out.println("time " + milliSec2Div + ", "  + timeRight + ", " + widgg.timeorg.timeAbsOnLastStrongDiv);
    1932            widgg.timeorg.sTimeAbsDiv[ixPixelTimeDiv] = "" + seconds;
    1933          } else if( widgg.timeorg.millisecPerFineDiv < 10000){  //less 10 sec for strong division, but more 1 sec
    1934            SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
    1935            String sTime = format.format(new Date( widgg.timeorg.timeAbsOnLastStrongDiv));
    1936            widgg.timeorg.sTimeAbsDiv[ixPixelTimeDiv] = sTime;
    1937          } else {
    1938            SimpleDateFormat format = new SimpleDateFormat("HH:mm");
    1939            String sTime = format.format(new Date( widgg.timeorg.timeAbsOnLastStrongDiv));
    1940            widgg.timeorg.sTimeAbsDiv[ixPixelTimeDiv] = sTime;
    1941          }
    1942          widgg.timeorg.pixelWrittenAfterStrongDiv = xPixel;
    1943          pixel2Div += widgg.timeorg.pixelPerTimeDiv;
    1944          milliSec2Div += widgg.timeorg.millisecPerDiv;
    1945        } else {
    1946          int xPixel =  (int)(pixel2FineDiv + 1.5f); //(int)(xTimeDivFine * widgg.timeorg.pixel7time + 0.5f) ;
    1947          widgg.timeorg.xPixelTimeDivFine[++ixPixelTimeDivFine] = xPixel;
    1948          //System.out.println("" + xPixel);
    1949        }
    1950        pixel2FineDiv += widgg.timeorg.pixelPerTimeFineDiv;
    1951        //xTimeDivFine += widgg.timeorg.shortTimePerDiv;
    1952      }
    1953      widgg.timeorg.timeLeftShowing = timeRight;  //for next call.
    1954      //
    1955      widgg.timeorg.xPixelTimeDiv[++ixPixelTimeDiv] = -1; //stopPoint;
    1956      widgg.timeorg.xPixelTimeDivFine[++ixPixelTimeDivFine] = -1; //stopPoint;
    1957      int time2 = timeRight;  //start time
    1958      int time = timeRight;
    1959      int dtime2 = 0;
    1960      //int dtime = 0;
    1961      //xViewPart = nrof pixel from right
    1962      //ixp1 counts from 0... right to left
    1963      //int nrofValues1 = nrofValues;
    1964      while(ixp < xViewPart       //Fills ixDataShown with the index to the data for each pixel.
    1965           && dtime2 <=0
    1966           && nrofPixel4Data >=0  //130328
    1967           ){ // && ixp1 >= ixp2){ //singularly: ixp1 < ixp2 if a faulty timestamp is found.
    1968        do{ //all values per 1 pixel
    1969          ixData -= widgg.adIxData; //decrement to older values in the data  ///
    1970          if(ixData == - widgg.adIxData && !widgg.dataOrg.bWrappedInBuffer){
    1971            //wrapped but data are not wrapped:
    1972            dtime2 = 1;  //same like time crack
    1973          }
    1974          else {
    1975            ixD = (ixData >> widgg.shIxiData) & widgg.mIxiData;  //the correct index in data.
    1976            time = widgg.timeValues[ixD];   //timestamp of that data point
    1977            dtime2 = time - time2;    //difference time from the last one. It is negative.
    1978            //dtime = time9 - time; //offset to first right point
    1979            if((dtime2) <0){  //from rigth to left, dtime2 <0 is expected
    1980              int dtime0 = timeRight - time; //time difference to the right pointt
    1981              int ixp3 = (int)(dtime0 * widgg.timeorg.pixel7time + 0.5f);  //calculate pixel offset from right, right =0
    1982              if(ixp3 > xViewPart){   //next time stamp is in the past of requested view
    1983                ixp3 = xViewPart;     //no more than requested nr of points.
    1984              }
    1985              nrofPixel4Data += (ixp3 - ixp);
    1986              if(ixp3 < ixp)
    1987                Debugutil.stop();
    1988              ixp = ixp3;
    1989              if(xpCursor1New == cmdSetCursor && ixData == ixDataCursor1){
    1990                xpCursor1New = ixp;                  //set cursor xp if the data index is gotten.
    1991              }
    1992              if(xpCursor2New == cmdSetCursor && ixData == ixDataCursor2){
    1993                xpCursor2New = ixp;
    1994              }
    1995            }
    1996          }
    1997        } while( ixp == ixp2   //all values for the same 1 pixel
    1998              && dtime2 <0     //stop at time crack to newer values.
    1999            //&& nrofValues >0
    2000              //&& ixData != ixDataRight  //no more as one wrap around, if dtime == 0 or is less
    2001               );
    2002        //
    2003        //if(ixp1 > ixp2 && ixp1 <= size_x){ //prevent drawing on false timestamp in data (missing data)
    2004        while(ixp2 < ixp) {
    2005          ixDataShown[ixp2] = ixData2;  //same index to more as one point.
    2006          this.nrofPixel4data[ixp2] = --nrofPixel4Data;
    2007          ixp2 +=1;
    2008        }
    2009        if(widgg.dataOrg.zPixelDataShown < ixp2){
    2010          widgg.dataOrg.zPixelDataShown = ixp2;    //max. value of shown data.
    2011        }
    2012        ixData2 = ixData;
    2013      }
    2014      if(ixp < xViewPart){
    2015        //System.out.println("GralCurveView large xViewPart");
    2016      }
    2017      //ixDataShown[++ixp2] = -1;      //the last
    2018      return ixp;
    2019    }
    2020
    2021  }
    2022
    2023
    2024
    2025
    2026
    2027
    2028
    2029  protected class GralCurveViewMouseAction implements GralMouseWidgetAction_ifc {
    2030
    2031    @Override public void mouse1Down(int key, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg)
    2032    {
    2033      if(xMousePixel < xWidgetSizePixel / 20 && yMousePixel > yWidgetSizePixel * 95/100){
    2034        //left side bottom
    2035        System.out.println("GralCurveView-Mousedown left bottom");
    2036        ((GraphicImplAccess)_wdgImpl).stopAndViewToPast();
    2037      } else if(xMousePixel > xWidgetSizePixel * 95 / 100 && yMousePixel > yWidgetSizePixel * 95/100){
    2038        //right side bottom
    2039        //System.out.println("GralCurveView-Mousedown rigth bottom");
    2040        ((GraphicImplAccess)_wdgImpl).viewToPresentOrGoIrRefresh();
    2041      } else if(xMousePixel < xWidgetSizePixel / 20 && yMousePixel < yWidgetSizePixel / 20){
    2042        //left side top
    2043        System.out.println("GralCurveView-Mousedown left top");
    2044        ((GraphicImplAccess)_wdgImpl).zoomToPast();
    2045      } else if(xMousePixel > xWidgetSizePixel * 95 / 100 && yMousePixel < yWidgetSizePixel / 20){
    2046        //right side top
    2047        //System.out.println("GralCurveView-Mousedown rigth top");
    2048        ((GraphicImplAccess)_wdgImpl).zoomToPresent();
    2049      } else {
    2050        if(!((GraphicImplAccess)_wdgImpl).selectTrack(xMousePixel, yMousePixel, xWidgetSizePixel, yWidgetSizePixel)){
    2051          ((GraphicImplAccess)_wdgImpl).setCursors(xMousePixel);
    2052        }
    2053        //if((key & KeyCode.mAddKeys) ==KeyCode.ctrl){
    2054        //}
    2055      }
    2056    }
    2057
    2058    @Override public void mouse1Up(int key, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg)
    2059    {
    2060      ((GraphicImplAccess)_wdgImpl).bMouseDownCursor1 = ((GraphicImplAccess)_wdgImpl).bMouseDownCursor2 = false;
    2061      //System.out.println("GralCurveView - MouseUp; ");
    2062    }
    2063
    2064    @Override
    2065    public void mouse2Down(int key, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel,
    2066        GralWidget widgg)
    2067    {
    2068      // TODO Auto-generated method stub
    2069
    2070    }
    2071
    2072    @Override
    2073    public void mouse2Up(int key, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel,
    2074        GralWidget widgg)
    2075    {
    2076      // TODO Auto-generated method stub
    2077
    2078    }
    2079
    2080    @Override
    2081    public void mouse1Double(int keyCode, int xMousePixel, int yMousePixel, int xWidgetSizePixel, int yWidgetSizePixel, GralWidget widgg)
    2082    {
    2083    }
    2084
    2085    @Override public boolean mouseMoved(int xMousePixel, int yMousePixel, int xWidgetPixelSize, int yWidgetPixelSize)
    2086    {
    2087      ((GraphicImplAccess)_wdgImpl).moveCursor(xMousePixel);
    2088      return true;
    2089    }
    2090
    2091
    2092  }
    2093
    2094  public GralUserAction actionZoomBetweenCursors = new GralUserAction("actionZoomBetweenCursors"){
    2095    @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){
    2096      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    2097        ((GraphicImplAccess)_wdgImpl).zoomBetweenCursors();
    2098      }
    2099      return true;
    2100    }
    2101  };
    2102
    2103  public GralUserAction actionZoomOut = new GralUserAction("actionZoomOut"){
    2104    @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){
    2105      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    2106        ((GraphicImplAccess)_wdgImpl).cursorUnzoom();
    2107      }
    2108      return true;
    2109    }
    2110  };
    2111
    2112
    2113  public GralUserAction actionCleanBuffer = new GralUserAction("actionCleanBuffer"){
    2114    @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){
    2115      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    2116        cleanBuffer();;
    2117      }
    2118      return true;
    2119    }
    2120  };
    2121
    2122
    2123  public GralUserAction actionGo = new GralUserAction("actionGo"){
    2124    @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){
    2125      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    2126        ((GraphicImplAccess)_wdgImpl).ixDataShowRight = ixDataWr;
    2127        common.bFreeze = false;
    2128      }
    2129      return true;
    2130    }
    2131  };
    2132
    2133  public GralUserAction actionPaintAll = new GralUserAction("actionPaintAll"){
    2134    @Override public boolean exec(int actionCode, GralWidget_ifc widgd, Object... params){
    2135      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    2136        ((GraphicImplAccess)_wdgImpl).setPaintAllCmd();
    2137      }
    2138      return true;
    2139    }
    2140  };
    2141
    2142}