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}