// Logana_revA3 Processing3 2021-10-30

import controlP5.*;
import java.util.*;
import processing.serial.*;

ControlP5 cp5;

ScrollableList[] SL = new ScrollableList[5];
RadioButton[] RB = new RadioButton[3];
Toggle[] TG = new Toggle[3];
Toggle bt;
Serial myPort = null;   // シリアルポート
List<String> portList;
  CColor bc = new CColor();
  CColor fc = new CColor();

String RxStr = "";      // 受信中の文字列
String waveStr = "";    // 描いている文字列
boolean indON = false;       // トリガインディケータ
boolean isDataRxing = false; // データ文字列受信中
boolean rptON = false;       // 連続モード
boolean nonTrg = false;      // 「トリガなし」出力
boolean cursors = false;     // 「cursors」出力
boolean dragging = false;    // ドラグ中か

int pntTrg = 100;       // トリガ位
int runMode = 0;        // 単発 連続
float zoomX = 1.0;      // スクリーン倍率
int sampRate = 5;       // サンプルレート
int repeetTimeUp;       // 連続実施までの時間

// 定数の定義 ----------------------------
final int sSpeed = 115200;    // シリアル通信速度

// データ描画用スクリーン位置定数
//final int srnLeft  = 40;
final int srnLeft  = 40;
final int srnTop   = 200;
//final int srnWidth = 550;
final int srnWidth = 512;
final int srnHight = 160;
final int srnRight = srnLeft + srnWidth;
final int srnBottom = srnTop + srnHight;
// コントロール枠位置定数
final int crlTop     = 50;
final int ctlTrgLeft  = 10;
final int ctlAquLeft  = ctlTrgLeft+200;
final int ctlRunLeft  = ctlAquLeft+200;
// スクロールバー定数
final int BarL = srnLeft;
final int BarT = srnBottom+2;
final int BarW = srnWidth;
final int BarH = 20;
final int BarR = BarL+BarW;
final int BarB = BarT+BarH;
final int wBarT = BarT+1;
final int wBarH = BarH-2;
// アクティブウインドバー定数
int wBarL = srnLeft;
int wBarW = srnWidth/2;
int wBarR = wBarL+wBarW;
int fBar  = 2;            // Barの縮尺
// データポインタ
int dStart = 0;     // 描き始める受信データ位置
int dEnd   = srnWidth;   // 描き終わる受信データ位置
int dStep  = 1;     // 描く受信データ間隔
int sStep = 1;     // 描くスクリーン間隔
//カーソルライン位置(データ座標)
int posCurDL = pntTrg;
int posCurDR = pntTrg;

final color cGray   = color(128);
final color cBlk   = color(0);
final color cWit   = color(255);
final color cRed   = color(255,0,0);
final color cGrn   = color(0,255,0);
final color cRed_r = color(0,255,255);
final color cGrn_r = color(255,9,255);

color cStr      = cWit;
color cActBtn   = color(50,150,240);
color cBgGroup  = cBlk;
color cDataLine = cGrn;
color cActRun   = color(150,100,0);
color cBakRun   = color(0,100,0);
color cForRun   = color(0,127,0);
color cTrig     = cRed;
color cIndOn    = cRed;
color cIndOff   = cBlk;
color cBgBtn    = color(80);
color cFgBtn    = cGray;
color cBkGnd    = color(50);
color cCursX = color(110,170,255);
color cCursY = color(255,140,255);

final List<String> trgList = Arrays.asList(
    "*", "1", "0", "/", "\");
final List<String>  sampList = Arrays.asList(
      "  1 us  (  1MHz)","  2 us  (500kHz)",
      "  5 us  (200kHz)"," 10 us  (100kHz)",
      " 20 us  ( 50kHz)"," 50 us  ( 20kHz)",
      "100 us  ( 10kHz)","200 us  (  5kHz)",
      "500 us  (  2kHz)","  1 ms  (  1kHz)");
final String[][] strDiv = {
    {"100us","200us","500us",   "1ms",  "2ms",
       "5ms", "10ms", "20ms",  "50ms","100ms"},
    { "50us","100us","250us", "500us",  "1ms",
     "2.5ms",  "5ms", "10ms",  "25ms", "50ms"},
    { "25us", "50us","125us", "250us","500us",
    "1.25ms","2.5ms",  "5ms","12.5ms", "25ms"}};
final int[] usPerDot = {
    1,2,5,10,20,50,100,200,500,1000};

void setup() {
  size(570, 430);
  cp5 = new ControlP5(this);
  cp5.setColorBackground(cBgBtn);   // 明るく見やすく
  PFont myFont = createFont("Verdana",12);
  ControlFont cf1 = new ControlFont(myFont);
  textFont(myFont);
  cp5.setFont(cf1);
  portList = Arrays.asList(Serial.list());
  int portIndex = portList.size()-1;

  // 各CHのトリガ条件の設定:( name, x, y, width, height )
  for(int i=3;i>0;i--){
    SL[i] = cp5.addScrollableList(
         "CH"+i,ctlTrgLeft+40,crlTop+(i*25),50,120)
     .setBarHeight(20) .setItemHeight(20)
     .addItems(trgList).setValue(0);
  }
  SL[1].setValue(3);
  // ポート選択の設定 ( name, x, y, width, height )
  SL[0] = cp5.addScrollableList(
                 "portSelect",20,20,220,160)
     .setBarHeight(20).setItemHeight(20)
     .addItems(portList).setValue(portIndex);
  // トリガと収集データの相対位置の設定ボタン
  RB[0] = cp5.addRadioButton(
          "r1SampPos",ctlAquLeft+50,crlTop+50)
     .setSize(15,15).setColorLabel(cStr)
     .setItemsPerRow(1).setSpacingColumn(100)
     .addItem("トリガの後方",0)
     .addItem("トリガの前後",4)
     .addItem("トリガの前方",5)
     .activate(0);
  // サンプルレートの設定
  SL[4] = cp5.addScrollableList(
          "srSampRate",ctlAquLeft+25,crlTop+25,150,220)
     .setBarHeight(20).setItemHeight(20)
     .addItems(sampList).setValue(5);
  // RUN/STOPボタンの設定:( name,初期値 )
  bt = cp5.addToggle("btnRun",false)
     .setPosition(ctlRunLeft+20,crlTop+15).setSize(90,30)
     .setColorActive(cActRun)     // ON色
     .setColorBackground(cBakRun)   // OFF色
     .setColorForeground(cForRun);  // マウス選択色
   bt.setCaptionLabel("RUN").getCaptionLabel()
     .align(ControlP5.CENTER, ControlP5.CENTER);
   // 単発/連続のラジオボタン
   RB[1] = cp5.addRadioButton(
                      "r2RunMode",ctlRunLeft+20,crlTop+60)
     .setSize(15,15).setColorLabel(cStr)
     .setItemsPerRow(2).setSpacingColumn(40)
     .addItem("単発",0).addItem("連続",1)
     .activate(0);
  // トリガなしのボタン
  TG[0] = cp5.addToggle("nonTrg",false)
     .setPosition(ctlRunLeft+20,crlTop+85).setSize(15,15)
     .setCaptionLabel("トリガなし");
   // カーソルOnOffボタン
   TG[1] = cp5.addToggle("cursors",false)
     .setPosition(135,srnTop-20).setSize(15,15)
     .setCaptionLabel("Cursors");
   // 背景色変更ボタン
   TG[2] = cp5.addToggle("whiteBack",false)
     .setPosition(ctlRunLeft+80,20).setSize(15,15)
     .setCaptionLabel("色 反転");

  for(Toggle t:TG){
    t.setColorLabel(cStr).getCaptionLabel()
     .align(ControlP5.RIGHT_OUTSIDE, ControlP5.CENTER)
     .toUpperCase(false) .setPaddingX(5)
     ;
  }

 // スケールのラジオボタン
 RB[2] = cp5.addRadioButton("rbZoom",50,400)
   .setSize(15,15).setColorLabel(cStr)
   .setColorLabel(cStr)
   .setItemsPerRow(3).setSpacingColumn(40)
   .addItem(".5x",0)
   .addItem("1x",1)
   .addItem("2x",2)
   .activate(1);
}

void draw() {
  // 連続モードの処理を行う
  if(rptON){
    // 繰り返し時間になったら
    if(repeetTimeUp < millis()){
      btnRun(true);     // RUNボタンを押す
      rptON = false;    // データ受信を待つ
    }
  }

  background(cBkGnd);
  drawScreen();     // 背景画面 描画
  drawData();       // データ線 描画
  indControl();     // 受信ライト 処理
  drawCursor();     // カーソル 描画
}

// ================================
// 画面背景を描く width height
// ================================
void drawScreen(){
  // ウインド枠を灰色にする
  stroke(cGray);
  fill(cBkGnd);
  rect(1,1,width-2, height-2);
  // データスクリーン ---------------------
  stroke(cGray);            // 枠を灰色に
  fill(cBgGroup);
  rect(srnLeft+1,srnTop+1,srnWidth,srnHight);
  // ch番号及び data GND level-------------
  stroke(cGray);
  fill(cStr);
  textSize(12);
  int j = 1;
  for(int vPos=srnTop+30;vPos<srnBottom;vPos+=30){
    text("Ch"+j++,srnLeft-32,vPos-5);
    line(srnLeft,vPos,srnRight,vPos);
  }
  // 小目盛は、10dot毎
  stroke(cGray);          //
  int scaleStart = srnLeft+10-(dStart*sStep%10);
  for(int i=scaleStart;i<srnRight;i+=10){
    line(i,srnTop,i,srnTop+5);
    line(i,srnBottom,i,srnBottom-5);
  }
  // 大目盛は、50dot毎
  scaleStart = srnLeft+50-(dStart*sStep%50);
  for(int i=scaleStart;i<srnRight;i+=50){
    line(i,srnTop,i,srnBottom);
  }
  // スクロール Bar -------------------
  stroke(cGray);                 // 枠の色
  fill(cBgGroup);                // 外ウインドの色
  rect(BarL,BarT,BarW,BarH);     // 外ウインド描く
  fill(cBkGnd);                  // 内ウインドの色
  rect(wBarL,wBarT,wBarW,wBarH); // 内ウインド描く

  // コントロール枠 -------------------------
  stroke(128);            // 枠を灰色に
  fill(cBgGroup);
  //rect(10,crlTop,260,110,10);
  rect(ctlTrgLeft,crlTop,190,110,10);
  rect(ctlAquLeft,crlTop,190,110,10);
  rect(ctlRunLeft,crlTop,140,110,10);
  // コントロール枠内ラベル ---------------------
  fill(cStr);
  textSize(12);
  text("<トリガ条件>",ctlTrgLeft+5,crlTop+18);
  text("Ch1",ctlTrgLeft+8,crlTop+40);
  text("Ch2",ctlTrgLeft+8,crlTop+65);
  text("Ch3",ctlTrgLeft+8,crlTop+90);
  text("*:なし", ctlTrgLeft+120,crlTop+30);
  text("1:Hi",  ctlTrgLeft+120,crlTop+46);
  text("0:Low", ctlTrgLeft+120,crlTop+62);
  text("/:立上り",ctlTrgLeft+120,crlTop+78);
  text("\:立下り",ctlTrgLeft+120,crlTop+94);
  text("<サンプル条件>",ctlAquLeft+5,crlTop+18);
  // トリガ線を引く -------------------
  vLine(cTrig, datX_appX(pntTrg));
  // Div時間の表示 -------------------
  String str;
  str = strDiv[int(zoomX)][sampRate] + " / Div";
  fill(cStr);
  textSize(12);
  text(str,srnLeft,srnTop-7);
  fill(cGray);
  text("211030 IWA",470,420);
}

// スクリーンに縦線を引く
void vLine(color c, int p){
  if(p == 0) return;
  stroke(c);
  line(p,srnTop,p,srnBottom);
}
// +++++++++++++++++++++++++++++++++
// 座標変換 データ座標 ー> アプリ座標
//  受信データの位置を作画のための座標に
// +++++++++++++++++++++++++++++++++
int datX_appX(int dX){
  int num = srnLeft + int((float)(dX - dStart) * zoomX);
  if(num <= srnLeft)  return 0;
  if(num >= srnRight) return 0;
   return num;
}

// +++++++++++++++++++++++++++++++++
// 座標変換 アプリ座標 ー> データ座標
//  作画のための座標を受信データの位置に
// +++++++++++++++++++++++++++++++++
int appX_datX(int aX){
   return int(float(aX - srnLeft)/zoomX)+dStart;
}

// データ線 描画 +++++++++++++++++++++++++++++
// waveStrを一文字ずつデータ線に書いていく
//++++++++++++++++++++++++++++++++++++++++++
void drawData(){
  int xPos;      // 描いている現データのX座標
  int dataNow;   // 処理している 現データ
  int dataNext;  // 次に処理する 次データ
  int diffData;  // 現データと次データの変位
  int ch;        // 描いているチャネル
  int testBit;   // 上記のチャネルbit位置
  int HiPos;     // 上記のチャネルHiレベル描画位置
  int LoPos;     // 上記のチャネルLowレベル描画位置

  // 各データの数だけ繰り返す ----------------------------
  int endPos = (waveStr.length())-2;
  if(endPos>dEnd)endPos = dEnd;        //
  xPos = srnLeft+int(zoomX);
  for (int i=dStart;i<endPos;i+=dStep){
    // データ毎の初期設定
    //    testBitをCh1を示すBit位置に戻す
    //    文字列から必要データを抜き出し、bit値に直す
    //    次のデータとのLogic変位を計算
    testBit = 1;
    dataNow  = int(waveStr.charAt(i)) & 0x1F;
    dataNext = int(waveStr.charAt(i+dStep)) & 0x1F;
    diffData = dataNow ^ dataNext;

    // 各チャネルの描画  5Ch分繰り返す ---------------------
    stroke(cDataLine);
    for (ch = 0; ch < 5; ch++){
      // Logic Hi Low の画描位置を計算
      HiPos = ch*30+10+srnTop;
      LoPos = HiPos + 20;
      // 現在のレベル位置に横線を引く
      if ((dataNow & testBit) == 0)
          line(xPos,LoPos,xPos+1,LoPos);
      else
          line(xPos,HiPos,xPos+1,HiPos);
      // 次のデータのレベルが変位していれば、縦線を引く
      if ((diffData & testBit) != 0)
        line(xPos + sStep,HiPos,xPos + sStep,LoPos);
      testBit <<= 1;      // 次のチャンネル準備
    }                     // 次のチャンネルへ
    xPos += sStep;        // 次のデータ描画位置
  }                       // 次のデータへ
}

// 受信ライト 処理 ++++++++++++++++++++++++++++
// indON の値に従い受信ライト On/Off
//++++++++++++++++++++++++++++++++++++++++++++
void indControl() {
  stroke(80);
  if(indON) fill(cIndOn);
    else    fill(cIndOff);
  circle(ctlRunLeft+120,crlTop+30, 10);
}

// =========================================
// カーソルを描く
// =========================================
void drawCursor(){
  if(!cursors) return;   // カーソル Off なら復帰
  String str;
  int nX,nY;
  // カーソル線の表示
  vLine(cCursX, datX_appX(posCurDL));
  vLine(cCursY, datX_appX(posCurDR));
  // カーソル数値を表示
  fill(cCursY);
  nY = timeToTrg(posCurDR);
  str = "Y = " + usms(nY);
  text(str,srnLeft+280,srnTop-7);
  fill(cCursX);
  nX = timeToTrg(posCurDL);
  str = "X = " + usms(nX);
  // X-Y の間隔を表示
  text(str,srnLeft+180,srnTop-7);
  str = "|X-Y| = " + usms(abs(nX-nY));
  str += hzkhz(abs(nX-nY));
  text(str,srnLeft+380,srnTop-7);
}
// カーソル位置からトリガまでの時間usを計算
int timeToTrg(int num){
  return (num - pntTrg) * usPerDot[sampRate];
}
// 数値を us/msで表示
String usms(int num){
  String str = "";
  int aN = abs(num);
  if(aN >= 100000){
    str = nf(float(num)/1000,0,0)+" ms";
  }else if(aN >= 10000){
    str = nf(float(num)/1000,0,1)+" ms";
  }else if(aN >= 1000){
    str = nf(float(num)/1000,0,2)+" ms";
  }else{
    str = nf(num,0)+" us";
  }
  return str;
}
// 数値を hz/khzで表示
String hzkhz(int num){
  if(num==0)return " (-- Hz)";
  String str = " (";
  float fnum = 1000000/float(num);
  if(fnum >= 1000){
    str += nf(fnum/1000,0,2)+" kHz)";
  }else if(fnum >= 100){
    str += nf(int(fnum),0)+" Hz)";
  }else{
    str += nf(fnum,0,1)+" Hz)";
  }
  return str;
}

// ================================
// シリアルポートが選択された時
// ================================
void portSelect(int n) {
  // 既に接続されていれば、切断して
  // 新しい選択項目は、index n で接続する
 if ( myPort != null ) myPort.stop();
 myPort = new Serial(this, portList.get(n), sSpeed);
 myPort.bufferUntil('\n');
}

// ================================
// シリアル受信した時
// ================================
void serialEvent(Serial port) {
  // 受信データがあれば、内容を確認して処理
  if ( port.available() > 0 ) {
    String str = port.readString();
    str = trim(str);
    if (str != null ){
      // PIC側送信完了なら ---------------
      if (str.equals("##")){
        waveStr = RxStr;       // 受信列を波形列とする
        indON = false;         // 受信ライト OFF
        isDataRxing = false;   // 受信中を 否
        if(runMode==0){        // 単発なら
          rptON = false;       //  rptONを 否
          bt.setValue(0);      //  ボタン緑へ
        }else{                 // 連続なら
          // 連続なら、500ms後に再びRUN操作をする
          // ために、repeetTimeUp を設定
          repeetTimeUp = millis()+500;
          rptON = true;       //  rptONを 真
        }
      // PIC側コマンド正常受信なら ---------------
      }else if (str.equals("#")){
        RxStr = "";          // 受信列をクリア
        isDataRxing = true;  // 受信中を 真
      // PIC側コマンドエラー受信なら ---------------
      }else if (str.equals("!")){
        RxStr = "";
      // PIC側STOP受信なら ---------------
      }else if (str.equals("!!")){
        RxStr = "";
      // PIC側のデータを受信した ---------------
      }else if (isDataRxing){
        indON = true;      // 受信ライト OFF
        RxStr += str;      // 受信列に追加
      }
    }
  }
}

// +++++++++++++++++++++++++++++++++
// RUN/STOPボタンを押した時
// +++++++++++++++++++++++++++++++++
void btnRun(boolean theFlag) {
  String cmdStr = "";
  // RUNボタンを押した時
  if(theFlag) {
     //waveStr = "";
     String cmdSamp,cmdTrg;

     bt.setCaptionLabel("STOP");
     cmdSamp = str((int)SL[4].getValue());
     cmdTrg  = str((int)RB[0].getValue());
     cmdStr = "#"
             + str((int)SL[1].getValue())
             + str((int)SL[2].getValue())
             + str((int)SL[3].getValue());
     if(cmdStr.equals("#000"))cmdTrg="3";
     if(nonTrg)cmdTrg  = "3";
     switch(cmdTrg){
       case "0":pntTrg = 100;break;
       case "3":pntTrg =   0;break;
       case "4":pntTrg = 550;break;
       case "5":pntTrg = 1000;break;
     }
     cmdStr += "#"+cmdSamp+cmdTrg+"\n" ;
  }
  // STOPボタンを押した時
  else {
     rptON = false;

     bt.setCaptionLabel("RUN");
     if (isDataRxing){
       cmdStr = "!";
     }
  }
  if(cmdStr != ""){
  //if(false){
      try{
        myPort.write(cmdStr);
      }
      catch(Exception e) {
        //msg("シリアル通信エラー");
        myPort.stop();
      }
  }
}

// サンプルレート変更 +++++++++++++++++++
// サンプルレートを変更した時、
// リスト項目の背景を少し明るく見やすく
// +++++++++++++++++++++++++++++++++++
void srSampRate(int n) {
  waveStr = "";            //
  posCurDL = pntTrg;        // カーソルのリセット
  posCurDR = pntTrg;
  bc.setBackground(cBgBtn);
  fc.setBackground(cFgBtn);
  // 古い選択項目の背景を戻し、新項目の背景を明るく
	cp5.get(ScrollableList.class, "srSampRate")
     .getItem(sampRate).put("color", bc);
  sampRate = n;
  cp5.get(ScrollableList.class, "srSampRate")
     .getItem(sampRate).put("color", fc);
}

// +++++++++++++++++++++++++++++++++
// サンプル位置を変更した時
//  非選択にならないよう処置
// +++++++++++++++++++++++++++++++++
void r1SampPos(int num) {
  if(num < 0){       // 非選択(-1)なら
    RB[0].activate(0);  // Item 0 を選択
    RB[0].setValue(0);
  }
}

// +++++++++++++++++++++++++++++++++
// 単発/連続を変更した時
// +++++++++++++++++++++++++++++++++
void r2RunMode(int num) {
  if(num < 0){
    RB[1].activate(0);
    RB[1].setValue(0);
    runMode = 0;
  }else{
    runMode = num;
  }
}

// +++++++++++++++++++++++++++++++++
// 倍率を変更した時
// +++++++++++++++++++++++++++++++++
void rbZoom(int num) {
  if(num < 0){
    RB[2].activate(1);
    RB[2].setValue(1);
    num = 1;
  }
  wBarL = 40;
  switch(num){
    case 0:        // x0.5
      wBarW = srnWidth;fBar  = 1;
      dStep = 2;  sStep = 1;
      zoomX = 0.5;
      break;
    case 1:        // x1
      wBarW = srnWidth/2;fBar  = 2;
      dStep = 1;  sStep = 1;
      zoomX = 1.0;
      break;
    case 2:        // x2
      wBarW = srnWidth/4;fBar  = 2;
      dStep = 1;  sStep = 2;
      zoomX = 2.0;
      break;
  }
    dStart = (wBarL-BarL)*fBar;
    dEnd = (wBarW- 1) * 2 + dStart;
}

// +++++++++++++++++++++++++++++++++
// 背景色を変更した時に実行
// +++++++++++++++++++++++++++++++++
void whiteBack(boolean theFlag) {
  if(theFlag==true) {      // 白 背景を選択
    cStr      = cBlk;
    cActBtn   = color(205,105,15);
    cBgGroup  = cWit;
    cDataLine = cGrn_r;
    cActRun   = color(105,155,255);
    cBakRun   = color(255,155,255);
    cForRun   = color(255,128,255);
    cTrig     = cRed_r;
    cIndOn    = cRed_r;
    cIndOff   = cWit;
    cBgBtn    = color(175);
    cBkGnd    = color(205);
    cCursX = color(145,85,0);
    cCursY = color(0,115,0);
  } else {                  // 黒 背景を選択
    cStr      = cWit;
    cActBtn   = color(50,150,240);
    cBgGroup  = cBlk;
    cDataLine = cGrn;
    cActRun   = color(150,100,0);
    cBakRun   = color(0,100,0);
    cForRun   = color(0,127,0);
    cTrig     = cRed;
    cIndOn    = cRed;
    cIndOff   = cBlk;
    cBgBtn    = color(80);
    cFgBtn    = cGray;
    cBkGnd    = color(50);
    cCursX = color(110,170,255);
    cCursY = color(255,140,255);
  }
  cp5.setColorActive(cActBtn)       // ON色
     .setColorBackground(cBgBtn)    // OFF色
     .setColorForeground(cActBtn);  // OFF色
  for(ScrollableList s:SL){
    s.setColorLabel(cStr)
     .setColorValue(cStr);
  }
  for(RadioButton r:RB)r.setColorLabel(cStr);
  for(Toggle t:TG)     t.setColorLabel(cStr);
  bt.setColorActive(cActRun)        // ON色
     .setColorBackground(cBakRun)   // OFF色
     .setColorForeground(cForRun)   // マウス選択色
     .setColorLabel(cStr);

  bc.setBackground(cBgBtn);
  fc.setBackground(cFgBtn);
  for(int i=0;i<10;i++){
    SL[4].getItem(i).put("color", bc);
  }
  SL[4].getItem(sampRate).put("color", fc);
}

// マウスプレス ++++++++++++++++++++++++
// カーソルの位置を決める
// +++++++++++++++++++++++++++++++++++
void mousePressed(MouseEvent e){
  // データスクリーン上か
  if(cursors && isInSrn()){
    if( e.getButton() == LEFT ){
     // println( "左が押された" );
      posCurDL = appX_datX(pmouseX);
    } else if( e.getButton() == RIGHT ){
     // println( "右が押された" );
      posCurDR = appX_datX(pmouseX);
    }
  }
}

// マウスドラッグ +++++++++++++++++++++++
// Barをドラッグしたら、その移動量だけ
// Barを移動させる
//++++++++++++++++++++++++++++++++++++
void mouseDragged(MouseEvent e){
  //スクロールバーをドラッグした場合
  if(mouseX == pmouseX)return;
  if((ptrInBar()==1)| dragging){
    dragging = true;
    wBarL += mouseX - pmouseX;
    if(wBarL<BarL)wBarL=BarL;
    if(wBarL>BarR-wBarW)wBarL=BarR-wBarW;
    // 波形データの描く範囲を計算
    dStart = (wBarL-BarL)*fBar;
    dEnd = (wBarW- 1) * 2 + dStart;
  }
}

// マウスリリース ++++++++++++++++++++++
// Barをドラッグ中に、Barエリアから外れても
// 処理を継続できるように追加
// +++++++++++++++++++++++++++++++++++
void mouseReleased(MouseEvent e){
  dragging = false;
}

// マウスポインター位置確認 ++++++=++++++=
// Mouse Pointer が
//  ScrollBarの外側なら       :0
//  ActiveWindowBarの内側なら :1
//  ScrollBarの左背景なら      :2
//  ScrollBarの右背景なら      :3
// +++++++++++++++++++++++++++=++++++=
int ptrInBar(){
  if(pmouseX < BarL)return 0;
  if(pmouseX > BarR)return 0;
  if(pmouseY < BarT)return 0;
  if(pmouseY > BarB)return 0;
  if(pmouseX < wBarL)return 2;
  if(pmouseX > (wBarL+wBarW)) return 3;
  return 1;
}

// マウスポインター位置確認 ++++++=++++++=
// Mouse Pointer が
//  データスクリーンの外側なら :false
//           内側なら :true
// +++++++++++++++++++++++++++=++++++=
boolean isInSrn(){
  if(pmouseX < srnLeft) return false;
  if(pmouseX > srnRight)return false;
  if(pmouseY < srnTop)  return false;
  if(pmouseY > srnBottom)return false;
  return true;
}