#!/bin/sh
# the next line restarts using wish \
exec wish86 "$0" "$@"

package require -exact snack 2.2
package require tkdnd 2.8
package require Thread

source proc-utawaview.tcl         ;# サブルーチン読み込み
source globalVar.tcl    ;# 大域変数読み込み

#---------------------------------------------------
# main - メインルーチン (初期化)

set sv(appname) utawaview
set sv(version) 0.8         ;# ソフトのバージョン番号
#set v(aepTool) "$topdir/plugins/utau_lib_analyze110/utau_lib_analyze.exe"
#set v(aepArg)  ""
set startup(initFile)     $topdir/$sv(appname)-init.tcl     ;# 通常ユーザがカスタマイズ内容を保存するファイル
set startup(sysIniFile)   $topdir/$sv(appname)-settings.ini ;# フォルダ使用履歴などをシステムが保存するファイル
set startup(textFile)     $topdir/message/$sv(appname)-text.tcl
set startup(procTextFile) $topdir/message/proc-text.tcl

# メッセージファイルを読む
foreach fn [list $startup(textFile) $startup(procTextFile)] {
  if {[file exists $fn]} {
    doReadInitFile $fn
  } else {
    tk_messageBox -message "can not find textFile ($fn)" \
      -title "Error" -icon error
    exit
  }
}

# スタートアップを読む
if {[file exists $startup(initFile)]} {
  doReadInitFile $startup(initFile)
}

# 引数チェック
# memo: oremo.tcl -- -option とするのが無難。--がないとwishのオプションと思われる様子
set i 0
while {$i < $argc} {
  set opt [lindex $argv $i]
  
  switch $opt {
    default {
      ;# アイコンにD&Dされたときの対応
      set opt [encoding convertfrom $opt]  ;# tcl/tk内部コード(utf-8)にする
      set opt [file normalize $opt]
      if [regexp {\.wav$} $opt] {       ;# wavファイルのD&D
        set v(inWav) $opt
      } else {
        puts "error: invalid option: $opt"
        usage
      }
    }
  }
  incr i
}

audioSettings ;# オーディオデバイス関連の初期化
fontSetting   ;# 日本語フォントを設定する
setSinScale   ;# 平均律の各音階の周波数を求める
readSysIniFile

#---------------------------------------------------
# スレッドの設定
tsv::set tid main [::thread::id]  ;# メインスレッドのIDを登録
;# F0抽出スレッドを生成
tsv::set tid f0   [::thread::create "source $topdir/thread/f0.tcl ; ::thread::wait"]
;# パワー抽出スレッドを生成
tsv::set tid power [::thread::create "source $topdir/thread/power.tcl ; ::thread::wait"]
tsv::set f0 status 0        ;# 0=何もせず、1=抽出中、2=エラー
tsv::set f0 f0 {}
tsv::set f0 extractedMax 0  ;# 抽出結果の最大値。抽出失敗時には0を入れる
tsv::set f0 extractedMin 0
tsv::set f0 showMax 0
tsv::set f0 showMin 0
tsv::set power status 0     ;# 0=何もせず、1=抽出中、2=エラー
tsv::set power power {}
tsv::set power powerMax 0
tsv::set power powerMin 0

#---------------------------------------------------
#
# 窓の設定
#
# snack::createIcons    ;# アイコンを使用する

# 1. 設定関係のフレーム
set s [frame .s]

## 2. 波形やスペクトルなどの図、保存フォルダなどを表示するフレーム
#frame .fig
#grid  .fig -row 0 -column 0 -sticky nwse

# 再生ボタンなど
set fTool [frame .fTool]
set menuF [label $fTool.menuFile -text $t(file)]
set menuS [label $fTool.menuShow -text $t(show)]
set menuO [label $fTool.menuOptn -text $t(option)]
set menuH [label $fTool.menuHelp -text $t(help)]
grid $menuF -row 0 -column 0
grid $menuS -row 0 -column 1
grid $menuO -row 0 -column 2
grid $menuH -row 0 -column 3
grid [button $fTool.play -image noatunplay22  -command togglePlay] -row 0 -column 4 -sticky nnwse
grid [button $fTool.stop -image player_stop22 -command playStop  ] -row 0 -column 5 -sticky nwse
grid [button $fTool.zoomUp -image viewmag+22   -highlightthickness 0 -command {
  changeWidth 1}] -row 0 -column 6 -sticky nnwse
grid [button $fTool.zoomDown -image viewmag-22 -highlightthickness 0 -command {
  changeWidth 0}] -row 0 -column 7 -sticky nnwse
grid [button $fTool.reload -image hotsync-22   -highlightthickness 0 -command {
  autoReload 1 }] -row 0 -column 8 -sticky nnwse
grid [button $fTool.utau -image appbookopen22 -text "UTAU" -command {
  # exec $::env(COMSPEC) /c start $topdir/tools/callUtau.exe "- ＵＴＡ" $v(utauExe) &
  if {[file executable $topdir/tools/callUtau.exe]} {
    exec $topdir/tools/callUtau.exe "- ＵＴＡ" $v(utauExe) &
    set tempDir [regsub -all -- {[\\]} $::env(TEMP) "/"]
    set v(inWav)   "$tempDir/utau1/temp.wav"
    autoReload 1
  } else {
    tk_messageBox -message "$topdir/tools/callUtau.exe is not exist." -title $t(.confm.errTitle) -icon warning
  }
}] -row 0 -column 9 -sticky nnwse
grid [label $fTool.mtimeU -text $t(time)        -padx 0 -fg $v(fg) -bg $v(bg)] -row 0 -column 10
grid [label $fTool.mtime -textvar v(inWavMtime) -padx 0 -fg $v(fg) -bg $v(bg)] -row 0 -column 11
#grid [label $fTool.bpmUnit -text ",BPM:"        -padx 0 -fg $v(fg) -bg $v(bg)] -row 0 -column 12
#grid [label $fTool.bpm   -textvar v(bpm)        -padx 0 -fg $v(fg) -bg $v(bg)] -row 0 -column 13
grid [button $fTool.reloadT -text $t(reload)    -padx 0 -fg $v(fg) -bg $v(bg) -relief flat -command setReloadWindow] -row 0 -column 14
grid [button $fTool.reloadV -textvar v(reload)  -padx 0 -fg $v(fg) -bg $v(bg) -relief flat -command setReloadWindow] -row 0 -column 15
grid [button $fTool.reloadUnit -text $t(sec)    -padx 0 -fg $v(fg) -bg $v(bg) -relief flat -command setReloadWindow] -row 0 -column 16
if {$v(reload) <= 0} {
  $fTool.reloadV configure -fg red
}

bind $fTool.utau <ButtonRelease-3> {
  if {$v(extEditor) != "" && [file executable $v(extEditor)]} {
    exec $v(extEditor) $v(inWav) &
  #  exec $topdir/tools/callUtau.exe "__dummy_string__" $v(extEditor) &
  } else {
    tk_messageBox -message "$v(extEditor) is not exist." -title $t(.confm.errTitle) -icon warning
  }
}

bind $menuF <Button-1> { PopUpMenuFile [winfo rootx $fTool.menuFile] [expr [winfo rooty $fTool.menuFile] + [winfo height $fTool.menuFile]]}
bind $menuS <Button-1> { PopUpMenuShow [winfo rootx $fTool.menuShow] [expr [winfo rooty $fTool.menuShow] + [winfo height $fTool.menuShow]]}
bind $menuO <Button-1> { PopUpMenuOptn [winfo rootx $fTool.menuOptn] [expr [winfo rooty $fTool.menuOptn] + [winfo height $fTool.menuOptn]]}
bind $menuH <Button-1> { PopUpMenuHelp [winfo rootx $fTool.menuHelp] [expr [winfo rooty $fTool.menuHelp] + [winfo height $fTool.menuHelp]]}
bind $menuF <Enter> { $fTool.menuFile configure -relief solid }
bind $menuS <Enter> { $fTool.menuShow configure -relief solid }
bind $menuO <Enter> { $fTool.menuOptn configure -relief solid }
bind $menuH <Enter> { $fTool.menuHelp configure -relief solid }
bind $menuF <Leave> { $fTool.menuFile configure -relief flat  }
bind $menuS <Leave> { $fTool.menuShow configure -relief flat  }
bind $menuO <Leave> { $fTool.menuOptn configure -relief flat  }
bind $menuH <Leave> { $fTool.menuHelp configure -relief flat  }
bind $fTool.reloadT    <Enter> { $fTool.reloadT    configure -relief solid }
bind $fTool.reloadV    <Enter> { $fTool.reloadV    configure -relief solid }
bind $fTool.reloadUnit <Enter> { $fTool.reloadUnit configure -relief solid }
bind $fTool.reloadT    <Leave> { $fTool.reloadT    configure -relief flat  }
bind $fTool.reloadV    <Leave> { $fTool.reloadV    configure -relief flat  }
bind $fTool.reloadUnit <Leave> { $fTool.reloadUnit configure -relief flat  }

# 2-1. 波形などを表示するフレーム
set wscrl [scrollbar .xscrl -orient horizontal -command {$c xview}]
if {[array names v "cWidth"] == 0} {
  set v(cWidth)  [expr $v(winWidth) - [winfo width $s] -47] ;# XPで開発していた時は-4だった
}
if {[array names v "cHeight"] == 0} {
  set v(cHeight) [expr $v(waveh) + $v(spech) + $v(powh) + $v(f0h) + $v(timeh)]
}
set c [canvas .c -width $v(cWidth) -height $v(cHeight) \
  -bg $v(bg) \
  -xscrollcommand "$wscrl set"
]
set cYaxis [canvas .cYaxis -width $v(yaxisw) -height $v(cHeight) \
  -bg $v(bg) \
]

# ↓gridの場合、窓横幅を$fTool横幅より狭くした時に$cが左上にスナップされなくなる問題が生じた。
#grid $fTool  -row 0 -column 0 -sticky new -columnspan 2
#grid $wscrl  -row 1 -column 1 -sticky wens
#grid $cYaxis -row 2 -column 0 -sticky nw
#grid $c      -row 2 -column 1 -sticky nw
#$c xview moveto 0
place $fTool -x 0 -y 0
update
place $wscrl -x $v(yaxisw) -y [winfo height $fTool] -width $v(cWidth)
update
place $cYaxis -x 0 -y [expr [winfo height $fTool] + [winfo height $wscrl]] -width $v(yaxisw) -height $v(cHeight)
place $c -x [expr $v(yaxisw) - 2] -y [expr [winfo height $fTool] + [winfo height $wscrl]] -width $v(cWidth) -height $v(cHeight)
place configure $c -bordermode inside
place configure $cYaxis -bordermode outside
place configure $wscrl -bordermode inside
place configure $fTool -bordermode outside
$c xview moveto 0
update

#frame .msg
#grid  .msg -row 0 -columnspan 2 -sticky new
#pack [label .msg.msg -textvar v(msg) -relief sunken -anchor nw] -fill x
#set v(msg) test

## 3. 収録中の音を保存するディレクトリを表示するフレーム
#set t(.inWav.midashi) "wavfile: "
#frame .inWav
#grid  .inWav -row 1 -columnspan 2 -sticky new
#label .inWav.midashi -text $t(.inWav.midashi) -fg $v(fg) -bg $v(bg)
#button .inWav.dir -textvar v(inWav)  \
#  -fg $v(fg) -bg $v(bg) -relief solid \
#  -command {
#    if [getWavFileName] {
#      resetDisplay
#    }
#  }
#button .inWav.sel -image snackOpen -highlightthickness 0 -bg $v(bg) \
#  -command {
#    if [getWavFileName] {
#      resetDisplay
#    }
#  }
#pack .inWav.midashi -side left
#pack .inWav.dir -side left -fill x -expand 1
#pack .inWav.sel -side left
#
#;#tabを押してこれらのウィジェットにフォーカスを当てると
#;#$cでF1などのキーバインドが効かなくなるのでフォーカスが当たらないようにする
#bind .inWav.dir <FocusIn> {focus .}
#bind .inWav.sel <FocusIn> {focus .}

## 4. メッセージを表示するフレーム
#frame .msg
#grid  .msg -row 3 -columnspan 2 -sticky new
#pack [label .msg.msg -textvar v(msg) -relief sunken -anchor nw] -fill x

# 5. 詳細設定用などの窓
set swindow .settings
set ioswindow .iosettings
set prgWindow .progress
set playRangeWindow .playRangeWindow
set bpmWindow .bpmWindow
set changeZoomWindow .changeZoomWindow 
set reloadWindow .reloadWindow
set alphaWindow .alphaWindow

#---------------------------------------------------
# バインド
bind . <Right>           { $c xview scroll  1 unit }
bind . <KeyPress-6>      { $c xview scroll  1 unit }
bind . <KeyPress-l>      { $c xview scroll  1 unit }
bind . <KeyPress-d>      { $c xview scroll  1 unit }
bind . <Left>            { $c xview scroll -1 unit }
bind . <KeyPress-4>      { $c xview scroll -1 unit }
bind . <KeyPress-a>      { $c xview scroll -1 unit }
bind . <KeyPress-h>      { $c xview scroll -1 unit }
bind . <KeyPress-q>      { $c xview moveto 0 }
bind . <KeyPress-w>      { $c xview moveto 0 }
bind . <space>               togglePlay
bind . <Control-p>           togglePlay
bind . <KeyPress-5>          togglePlay
bind . <KeyPress-c>          {     ;# 波形再読み込み
  set power(fid) ""
  set f0(fid) ""
  readWavFile
  Redraw all
}

bind . <Control-r> { autoReload 1 }
bind . <F5> { autoReload 1 }
bind . <Alt-F4>  Exit
bind . <KeyRelease-U> autoSetTimeScale

bind $cYaxis <ButtonRelease-1> { ;# 時間軸の単位変更
  if {%y <= $v(timeh)} {
    if {$v(timeUnit) == "Bar"} {
      set v(timeUnit) "Sec."
    } else {
      set v(timeUnit) "Bar"
    }
    Redraw scale
  }
}
bind $cYaxis <ButtonRelease-3> { ;# 時間軸の設定
  setBPMWindow
}

# コンソールの表示
bind . <Control-Alt-d> {
  if {$conState} {
    console hide
    set conState 0
  } else {
    console show
    set conState 1
  }
}
bind . <Control-D> {   ;# 某タブレットキーボード用。
  if {$conState} {
    console hide
    set conState 0
  } else {
    console show
    set conState 1
  }
}

# マウスホイール
bind . <MouseWheel> {
      # 波形を横スクロール
      if {%D > 0} {
        $c xview scroll -1 unit
      } else {
        $c xview scroll 1 unit
      }
}

# 横スクロール
bind . <Shift-MouseWheel> {
  if {%D > 0} {
    $c xview scroll -1 unit
  } else {
    $c xview scroll 1 unit
  }
}

# 波形横方向拡大縮小 
bind . <Control-MouseWheel> {
  if {%D > 0} {
    changeWidth 1 [expr %x  - 2] ;# 上回転で拡大
  } else {
    changeWidth 0 [expr %x  - 2] ;# 縮小
  }
}
bind . <F11> { changeWidth 0 [expr %x - $v(yaxisw) - 6]}  ;# 縮小
bind . <F12> { changeWidth 1 [expr %x - $v(yaxisw) - 6]}  ;# 拡大

# 波形拡大縮小(縦方向, Control-Shift+マウスホイール)
bind . <Control-Shift-MouseWheel> {
  if {%y > [winfo height $wscrl] && %x > [winfo width $s]} {
    # マウスが波形画面上にある場合
    if {%D > 0} {
      set inc -20    ;# 上向き回転
    } else {
      set inc +20    ;# 下向き回転
    }
    set my [expr %y - [winfo height $wscrl]]
    if {$my <= [expr $v(timeh) + $v(waveh)]} {
      # 波形を拡大・縮小
      incr v(waveh) $inc
      if {$v(waveh) < $v(wavehmin)} {
        set v(waveh) $v(wavehmin)
      }
    } elseif {$my <= [expr $v(timeh) + $v(waveh) + $v(spech)]} {
      # スペクトルを拡大・縮小
      incr v(spech) $inc
      if {$v(spech) < $v(spechmin)} {
        set v(spech) $v(spechmin)
      }
    } elseif {$my <= [expr $v(timeh) + $v(waveh) + $v(spech) + $v(powh)]} {
      # パワーを拡大・縮小
      incr v(powh) $inc
      if {$v(powh) < $v(powhmin)} {
        set v(powh) $v(powhmin)       ;# 縮小の最小値
      }
    } elseif {$my <= [expr $v(timeh) + $v(waveh) + $v(spech) + $v(powh) + $v(f0h)]} {
      # F0を拡大・縮小
      incr v(f0h) $inc
      if {$v(f0h) < $v(f0hmin)} {
        set v(f0h) $v(f0hmin)       ;# 縮小の最小値
      }
    }
    set v(winHeight) [expr [winfo y $c] + $v(waveh) + $v(spech) + $v(powh) + $v(f0h) + $v(timeh)]
    wm geometry . "$v(winWidth)x$v(winHeight)"
    Redraw scale
  }
}

set resizingAxis ""  ;# リサイズ中のパネル略称を入れる

bind $c <ButtonPress-1> {
  if {$region(status) != "" || $v(sndLength) <= 0} break

  # マウスで各パネルを拡大縮小
  if {[expr abs(%y - $v(timeh) - $v(waveh))] < 5} {
    set resizingAxis "w"
    set region(status) "paneBorerPressed"
    $c configure -cursor based_arrow_down
  } elseif {[expr abs(%y - $v(timeh) - $v(waveh) - $v(spech))] < 5} {
    set resizingAxis "s"
    set region(status) "paneBorerPressed"
    $c configure -cursor based_arrow_down
  } elseif {[expr abs(%y - $v(timeh) - $v(waveh) - $v(spech) - $v(powh))] < 5} {
    set resizingAxis "p"
    set region(status) "paneBorerPressed"
    $c configure -cursor based_arrow_down
  } elseif {[expr abs(%y - $v(timeh) - $v(waveh) - $v(spech) - $v(powh) - $v(f0h))] < 5} {
    set resizingAxis "f"
    set region(status) "paneBorerPressed"
    $c configure -cursor based_arrow_down
  } else {
    addRegion1 [expr %x - 2] [expr %y - 2]
  }
}

#
# キャンバスの空いている領域をクリックしたとき
#
proc addRegion1 {x y} {
  global region c v wscrl
  #puts "addRegion1: $region(status)"

  if {$region(status) != ""} return

  set objName "region$region(seq)"
  set cLeftPoint [expr $v(sndLength) * [lindex [$wscrl get] 0] * $v(wavepps)]
  set x [gridSnap [expr $cLeftPoint + $x] "left"]

  $c create rectangle $x $y $x $y -fill yellow -outline #60a060 -stipple gray25 -tags [list $objName regions]
  set region(status) "vacancyPressed"
}

#
# 指定したx座標を近傍のグリッドにスナップする
# mode: left  ...クリックした場所より左側のグリッドを返す
# mode: near  ...クリックした場所の最近傍のグリッドを返す
# mode: right ...クリックした場所より右側のグリッドを返す
#
proc gridSnap {x {mode "near"}} {
  global v

  if {$v(gridNote) <= 0} { return $x }

  # グリッド長(単位:画素)
  set barPixel  [expr $v(wavepps) * 60.0 / $v(bpm)]
  set gridPixel [expr 4.0 * $barPixel / $v(gridNote)]
  set barPrePixel [expr $v(barPre) * $barPixel]

  # グリッド長が2分音符以上で、冒頭音符の先行発声の領域を選んでいる場合
  if {$x < $barPrePixel} {
    set sho [expr int(($barPrePixel - $x) / $gridPixel)]
    set L   [expr $sho * $gridPixel]
    set R   [expr ($sho +1) * $gridPixel]
    if {$R > $barPrePixel} {
      set R $barPrePixel
    }
    if {$mode == "left" } { return [expr $barPrePixel - $R] }
    if {$mode == "right"} { return [expr $barPrePixel - $L] }
    if {[expr abs($x - $L)] < [expr abs($x - $R)]} {
      return [expr $barPrePixel - $L]
    } else {
      return [expr $barPrePixel - $R]
    }
  }

  # 0小節目以降を選んでいる場合
  set sho [expr int(($x - $barPrePixel) / $gridPixel)]
  set L   [expr $sho * $gridPixel + $barPrePixel]
  set R   [expr ($sho +1) * $gridPixel + $barPrePixel]

  if {$mode == "left" } { return $L }
  if {$mode == "right"} { return $R }
  if {[expr abs($x - $L)] < [expr abs($x - $R)]} {
    return $L
  } else {
    return $R
  }
}

#
# キャンバス内でクリックしてドラッグしたとき
#
proc addRegion2 {x y} {
  global region c v wscrl
  #puts "addRegion2: $region(status)"

  set objName "region$region(seq)"
  set coords [$c coords $objName]
  set x1 [lindex $coords 0]
  set y1 [lindex $coords 1]

  set cLeftPoint [expr $v(sndLength) * [lindex [$wscrl get] 0] * $v(wavepps)]
  set x2 [gridSnap [expr $cLeftPoint + $x] right]

  $c coords $objName $x1 $y1 $x2 $y
  set region(status) "vacancyDragged"
}

#
# キャンバス内でマウスを押して(ドラッグして)離したとき
#
proc addRegion3 {x y} {
  global region c v wscrl
  #puts "addRegion3: $region(status)"

#  if {$region(status) != "vacancyDragged"} {
#    set region(status) ""
#    return
#  }
  set objName "region$region(seq)"

  set coords [$c coords $objName]
  set x1 [lindex $coords 0]
  set y1 [lindex $coords 1]
  set x2 [lindex $coords 2]
  set y2 [lindex $coords 3]
  if {[expr abs($x1 - $x2)] <= 2 || [expr abs($y1 - $y2)] <= 5} {
    $c delete $objName
    set region(status) ""
    update
    return
  }

  $c bind $objName <ButtonPress-1>   {objPressed  [expr %x - 2] [expr %y - 2]}
  $c bind $objName <ButtonPress-2>   {objPressed  [expr %x - 2] [expr %y - 2]}
  $c bind $objName <ButtonPress-3>   {objDelete1  [expr %x - 2] [expr %y - 2]}
  $c bind $objName <B1-Motion>       {objDragged  [expr %x - 2] [expr %y - 2]}
  #$c bind $objName <B3-Motion>       {puts "dr: $region(status)"}
  $c bind $objName <ButtonRelease-1> {objReleased [expr %x - 2] [expr %y - 2]}
  $c bind $objName <ButtonRelease-2> {objReleased [expr %x - 2] [expr %y - 2]}
  $c bind $objName <ButtonRelease-3> {objDelete2  [expr %x - 2] [expr %y - 2]}
  $c raise $objName
  incr region(seq)
  set region(status) ""
}

proc addRegion3b {x y} {
  global region c v wscrl
  #puts "addRegion3b: $region(status)"

#  if {$region(status) != "vacancyPressed"} {
#    set region(status) ""
#    return
#  }
  set objName "region$region(seq)"
  $c delete $objName
  if {$y < $v(timeh)} {
    playFromHere $x
  } else {
    # togglePlay
  }
  set region(status) ""
  update
}

#
# マウス位置から末尾まで再生する
#
proc playFromHere {x} {
  global v wscrl
  set cLeftPoint [expr $v(sndLength) * [lindex [$wscrl get] 0] * $v(wavepps)]
  set x [expr $cLeftPoint + $x]
  set playS [expr int((double($x) / $v(wavepps) - $v(wavOffsetSec)) * $v(sampleRate))]
  togglePlay $playS -1
}

#
# 範囲選択領域をクリックしたとき
#
proc objPressed {x y} {
  global region c v wscrl
  #puts "objPressed: $region(status)"

  if {$region(status) != ""} return

  set region(x0) $x  ;# クリックしたときの位置をひかえる
  set region(y0) $y  ;# クリックしたときの位置をひかえる
  set coords [$c coords current]
  set x1 [lindex $coords 0]
  set y1 [lindex $coords 1]
  set x2 [lindex $coords 2]
  set y2 [lindex $coords 3]
  set cLeftPoint [expr $v(sndLength) * [lindex [$wscrl get] 0] * $v(wavepps)]
  set cx [expr $cLeftPoint + $x]
  if {[expr $cx - $x1] <= 5} {
    if {[expr $y2 - $y] <= 5} {
      set region(status) "objBorderPressedLB"
    } elseif {[expr $y - $y1] <= 5} {
      set region(status) "objBorderPressedLT"
    } else {
      set region(status) "objBorderPressedL"
    }
  } elseif {[expr $x2 - $cx] <= 5} {
    if {[expr $y2 - $y] <= 5} {
      set region(status) "objBorderPressedRB"
    } elseif {[expr $y - $y1] <= 5} {
      set region(status) "objBorderPressedRT"
    } else {
      set region(status) "objBorderPressedR"
    }
  } elseif {[expr $y - $y1] <= 5} {
    set region(status) "objBorderPressedT"
  } elseif {[expr $y2 - $y] <= 5} {
    set region(status) "objBorderPressedB"
  } else {
    set region(status) "objPressed"
    set region(objT) [expr $y - $y1]
    set region(objB) [expr $y2 - $y]
    set region(objL) [expr $cx - $x1]
    set region(objW) [expr $x2 - $x1]
  }
}

proc objDelete1 {x y} {
  global region c v
  #puts "objDelete1: $region(status)"

  if {$region(status) != ""} return
  set region(status) "objDelete"
}

proc objDelete2 {x y} {
  global region c v wscrl
  #puts "objDelete2: $region(status)"

  if {$region(status) != "objDelete"} return
  set coords [$c coords current]
  set x1 [lindex $coords 0]
  set y1 [lindex $coords 1]
  set x2 [lindex $coords 2]
  set y2 [lindex $coords 3]
  set cLeftPoint [expr $v(sndLength) * [lindex [$wscrl get] 0] * $v(wavepps)]
  set x [expr $cLeftPoint + $x]
  if {$x1 <= $x && $x <= $x2 && $y1 <= $y && $y <= $y2} {
    $c delete current
  }
  set region(status) ""
}

#
# 選択領域をドラッグしたとき
#
proc objDragged {x y} {
  global region c v wscrl
  #puts "objDragged: $region(status)"

  set coords [$c coords current]
  set x1 [lindex $coords 0]
  set y1 [lindex $coords 1]
  set x2 [lindex $coords 2]
  set y2 [lindex $coords 3]
  set cLeftPoint [expr $v(sndLength) * [lindex [$wscrl get] 0] * $v(wavepps)]
  set nx [gridSnap [expr $cLeftPoint + $x] near]

  switch $region(status) {
    "objBorderDraggedL"  { $c coords current $nx $y1 $x2 $y2 }
    "objBorderDraggedLT" { $c coords current $nx $y  $x2 $y2 }
    "objBorderDraggedLB" { $c coords current $nx $y1 $x2 $y  }
    "objBorderDraggedR"  { $c coords current $x1 $y1 $nx $y2 }
    "objBorderDraggedRT" { $c coords current $x1 $y  $nx $y2 }
    "objBorderDraggedRB" { $c coords current $x1 $y1 $nx $y  }
    "objBorderDraggedT"  { $c coords current $x1 $y  $x2 $y2 }
    "objBorderDraggedB"  { $c coords current $x1 $y1 $x2 $y  }
    "objDragged"         { set nL [gridSnap [expr $cLeftPoint + $x - $region(objL)] near]
                           set nR [expr $nL + $region(objW)]
                           $c coords current $nL [expr $y - $region(objT)] $nR [expr $y + $region(objB)]
                         }

    "objBorderPressedL"  { dragStart $x $y "objBorderDraggedL"  }
    "objBorderPressedLT" { dragStart $x $y "objBorderDraggedLT" }
    "objBorderPressedLB" { dragStart $x $y "objBorderDraggedLB" }
    "objBorderPressedR"  { dragStart $x $y "objBorderDraggedR"  }
    "objBorderPressedRT" { dragStart $x $y "objBorderDraggedRT" }
    "objBorderPressedRB" { dragStart $x $y "objBorderDraggedRB" }
    "objBorderPressedT"  { dragStart $x $y "objBorderDraggedT"  }
    "objBorderPressedB"  { dragStart $x $y "objBorderDraggedB"  }
    "objPressed"         { dragStart $x $y "objDragged" }
  }
}

#
# 選択領域を選択後に数ピクセル以上マウスをドラッグしたときのみドラッグ状態に変える
#
proc dragStart {x y newState} {
  global region
  set T 2
  if {[expr abs($region(x0) - $x)] > $T || [expr abs($region(y0) - $y)] > $T} {
    set region(status) $newState
  }
}

proc objReleased {x y} {
  global region v c wscrl
  #puts "objReleased: $region(status)"
  if {$region(status) == "objPressed"} {
    set coords [$c coords current]
    set x1 [lindex $coords 0]
    set y1 [lindex $coords 1]
    set x2 [lindex $coords 2]
    set y2 [lindex $coords 3]
    set cLeftPoint [expr $v(sndLength) * [lindex [$wscrl get] 0] * $v(wavepps)]
    set x [expr $cLeftPoint + $x]
    if {$x1 <= $x && $x <= $x2 && $y1 <= $y && $y <= $y2} {
      set playS [expr int((double($x1) / $v(wavepps) - $v(wavOffsetSec)) * $v(sampleRate))]
      set playE [expr int((double($x2) / $v(wavepps) - $v(wavOffsetSec)) * $v(sampleRate))]
      if {$playE > 0} {
        $c raise current
        togglePlay $playS $playE
      }
    }
  } elseif {$region(status) == "objBorderPressedL"} {
  } elseif {$region(status) == "objBorderPressedLT"} {
  } elseif {$region(status) == "objBorderPressedLB"} {
  } elseif {$region(status) == "objBorderPressedR"} {
  } elseif {$region(status) == "objBorderPressedRT"} {
  } elseif {$region(status) == "objBorderPressedRB"} {
  } elseif {$region(status) == "objBorderPressedT"} {
  } elseif {$region(status) == "objBorderPressedB"} {
  } elseif {$region(status) == "objDragged"} {
  }
  set region(status) ""
}

;# マウスで各パネルを拡大縮小
bind $c <B1-Motion> {
  #puts "b1-motion: $region(status)"
  if {$region(status) == "vacancyPressed" || $region(status) == "vacancyDragged"} {
    addRegion2 [expr %x - 2] [expr %y - 2]
  }
}

bind . <ButtonRelease-1> {
  #puts "ButtonRelease-1: $region(status)"
  if {$region(status) == "vacancyDragged"} {
    addRegion3 [expr %x - 2] [expr %y - 2]
  } elseif {$region(status) == "vacancyPressed"} {
    addRegion3b [expr %x - 2] [expr %y - 2]
  } elseif {$resizingAxis != ""} {
    if {$resizingAxis == "w"} {
      set v(waveh) [expr %y - $v(timeh)]
      if {$v(waveh) < $v(wavehmin)} {
        set v(waveh) $v(wavehmin)
      }
    } elseif {$resizingAxis == "s"} {
      set v(spech) [expr %y - $v(timeh) - $v(waveh)]
      if {$v(spech) < $v(spechmin)} {
        set v(spech) $v(spechmin)
      }
    } elseif {$resizingAxis == "p"} {
      set v(powh) [expr %y - $v(timeh) - $v(waveh) - $v(spech)]
      if {$v(powh) < $v(powhmin)} {
        set v(powh) $v(powhmin)
      }
    } elseif {$resizingAxis == "f"} {
      set v(f0h) [expr %y - $v(timeh) - $v(waveh) - $v(spech) - $v(powh)]
      if {$v(f0h) < $v(f0hmin)} {
        set v(f0h) $v(f0hmin)
      }
    }
    set v(winHeight) [expr [winfo y $c] + $v(waveh) + $v(spech) + $v(powh) + $v(f0h) + $v(timeh)]
    wm geometry . "$v(winWidth)x$v(winHeight)"
    Redraw scale
    $c configure -cursor arrow  ; update
    set resizingAxis ""
    set region(status) ""
  }
}

bind $c <ButtonPress-2> {
  if {$region(status) != ""} break
  set region(status) "middlePressed-2"
}
bind $c <B2-Motion> {
  if {$region(status) != "middlePressed-2"} break
  set region(status) "middleDragged-2"
}
bind $c <ButtonRelease-2> {
  if {$region(status) == "middlePressed-2"} {
    if {%y < $v(timeh)} {
      playFromHere [expr %x - 2]
    } else {
      togglePlay
    }
  }
  set region(status) ""
}


;# 右クリックメニュー
bind $c <ButtonPress-3> {
  if {$region(status) == ""} {
    PopUpMenu %X %Y %x %y
  }
}

dnd bindtarget . text/uri-list <Drop> {procDnd %D}

#---------------------------------------------------
# メニューの設定
set clickMenuFile   [menu .popmenuFile    -tearoff true]
set clickMenuFileC  [menu .popmenuFile.c  -tearoff false]
set clickMenuFileC2 [menu .popmenuFile.c2 -tearoff false]
set clickMenuShow   [menu .popmenuShow    -tearoff true]
set clickMenuShowC  [menu .popmenuShow.c  -tearoff true]
set clickMenuShowC2 [menu .popmenuShow.c2 -tearoff true]
set clickMenuOptn   [menu .popmenuOptn    -tearoff true]
set clickMenuHelp   [menu .popmenuHelp    -tearoff true]
set rclickMenu      [menu .popmenu   -tearoff false]
set rclickMenuZ     [menu .popmenu.z -tearoff true]
set rclickMenuG     [menu .popmenu.g -tearoff true]
set rclickMenuP     [menu .popmenu.p -tearoff true]

#---------------------------------------------------
# 初期化
set f0(showMax) [tone2freq "$f0(showMaxTone)$f0(showMaxOctave)"]
set f0(showMin) [tone2freq "$f0(showMinTone)$f0(showMinOctave)"]

set v(winHeight) [expr [winfo y $c] + $v(waveh) + $v(spech) + $v(powh) + $v(f0h) + $v(timeh) + 4]
wm geometry . "$v(winWidth)x$v(winHeight)"
wm protocol . WM_DELETE_WINDOW Exit
wm title . "$sv(appname) $sv(version)"
wm resizable . 1 1                             ;# メイン窓幅を変更可能にする
# set sv(winWidthMin)  200
# set sv(winHeightMin) 200
# wm minsize . $sv(winWidthMin) $sv(winHeightMin)
wm attributes . -alpha $v(alpha)

proc changeWindowBorder {} {
  global v sv c

  update
  set w [winfo width  .]
  set h [winfo height .]
  if {$v(winWidth) == $w && $v(winHeight) == $h} return

  set v(winWidth)  $w
  set v(winHeight) $h
  set cWidthOld $v(cWidth)
  # set v(cWidth)  [expr $w - $v(yaxisw) - 8]  ;# 4はキャンバス境界のマージン
  set v(cWidth)  [expr $w - $v(yaxisw)]  ;# place用
  ;#
  if {$v(sndLength) > 0} {
    if {$v(showWhole) != 0 || $v(wavepps) == [expr $cWidthOld / $v(sndLength)]} {
      set waveppsOld $v(wavepps)
      set v(wavepps) [expr $v(cWidth) / $v(sndLength)]  ;# wav全体を表示
      set v(wavOffsetPixel) [expr $v(wavOffsetSec) * $v(wavepps)]
      $c scale regions 0 0 [expr $v(wavepps) / $waveppsOld] 1
    }
  }
  set diff [expr $v(winHeight) - ([winfo y $c] + [winfo height $c])]
  if {$diff > 0} {
    if {$v(f0h) > 0} {
      set v(f0h) [expr $v(f0h) + $diff]
    } elseif {$v(powh) > 0} {
      set v(powh) [expr $v(powh) + $diff]
    } elseif {$v(spech) > 0} {
      set v(spech) [expr $v(spech) + $diff]
    } elseif {$v(waveh) > 0} {
      set v(waveh) [expr $v(waveh) + $diff]
    }
  } elseif {$diff < 0} {
    if {$v(f0h) > 0} {
      set old $v(f0h)
      set v(f0h) [expr $v(f0h) + $diff]
      if {$v(f0h) < $v(f0hmin)    } { set v(f0h) $v(f0hmin) }
      set diff [expr $diff - ($v(f0h) - $old)]
    }
    if {$diff < 0 && $v(powh) > 0} {
      set old $v(powh)
      set v(powh) [expr $v(powh) + $diff]
      if {$v(powh) < $v(powhmin)  } { set v(powh) $v(powhmin) }
      set diff [expr $diff - ($v(powh) - $old)]
    }
    if {$diff < 0 && $v(spech) > 0} {
      set old $v(spech)
      set v(spech) [expr $v(spech) + $diff]
      if {$v(spech) < $v(spechmin)} { set v(spech) $v(spechmin) }
      set diff [expr $diff - ($v(spech) - $old)]
    }
    if {$diff < 0 && $v(waveh) > 0} {
      set old $v(waveh)
      set v(waveh) [expr $v(waveh) + $diff]
      if {$v(waveh) < $v(wavehmin)} { set v(waveh) $v(wavehmin) }
      set diff [expr $diff - ($v(waveh) - $old)]
    }
  }
  # set winHeightMinNew [expr [winfo y $c] + $v(timeh)]
  # if {$v(showWave)} { set winHeightMinNew [expr $winHeightMinNew + $v(wavehmin)] }
  # if {$v(showSpec)} { set winHeightMinNew [expr $winHeightMinNew + $v(spechmin)] }
  # if {$v(showPow) } { set winHeightMinNew [expr $winHeightMinNew + $v(powhmin) ] }
  # if {$v(showF0)  } { set winHeightMinNew [expr $winHeightMinNew + $v(f0hmin)  ] }
  # wm minsize . $sv(winWidthMin) $winHeightMinNew
  Redraw scale
  update
}

if {$v(alwaysOnTop)} {
  wm attributes . -topmost 1
} else {
  wm attributes . -topmost 0
}
Redraw all
if {$v(reload) > 0} {
  autoReload    ;# 自動更新開始
} else {
  autoReload 1  ;# 手動で一度強制更新
}

after 1000 { bind . <Configure> {changeWindowBorder} } ;# 1sec後にbind。afterがないとボタンなどのwidgetが表示されないケースがあったため
