########################################################################
#
# "main_tcl" : Tvi, メイン Tcl プロシージャ群
#
# Copyright (c) 1998,1999  Motomu Nakashima
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option) 
# any later version.
#
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
# GNU General Public License for more details.
#
########################################################################

# ウインドゥの高さが resize によって変えられたときに，
# 大域変数 columns, lines  を再設定する
proc reconfig_lines { } {
    global columns lines
    set wm_value [wm geometry . ]
    set wm_value [string trimright $wm_value 0123456789 ]
    set wm_value [string trimright $wm_value +- ]
    set wm_value [string trimright $wm_value 0123456789 ]
    set wm_value [string trimright $wm_value +- ]
    set wm_value [string trimleft $wm_value 0123456789 ]
    set wm_value [string trimleft $wm_value x ]
    set lines $wm_value

    set wm_value [wm geometry . ]
    set wm_value [string trimright $wm_value 0123456789 ]
    set wm_value [string trimright $wm_value +- ]
    set wm_value [string trimright $wm_value 0123456789 ]
    set wm_value [string trimright $wm_value +- ]
    set wm_value [string trimright $wm_value 0123456789 ]
    set wm_value [string trimright $wm_value x ]
    set columns $wm_value
}

# Button-1 が押されたときの動作
proc tkTextButton1 {w x y} {
    global tkPriv

    set tkPriv(selectMode) char
    set tkPriv(mouseMoved) 0
    set tkPriv(pressX) $x
    # $w mark set insert [tkTextClosestGap $w $x $y]
    # $w mark set anchor insert
    $w mark set anchor [tkTextClosestGap $w $x $y]
    # if {[$w cget -state] == "normal"} {focus $w}
}

# セレクションのペースト
proc tkTextPaste {w} {
    # $w mark set insert [tkTextClosestGap $w $x $y]
    catch {$w insert insert [selection get -displayof $w]}
    if {[$w cget -state] == "normal"} {focus $w}
}

# bell を長めに鳴らすプロシージャ
# 一旦 .garbage に focus しておいて，一定時間後にまた戻す
proc belll { } { 
   set now_focus [ focus ]
   focus .garbage
   bell
   after 150
   update
   focus "$now_focus"
}

# 漢字のON/OFF
proc kanji_onoff { } {
   global kanji_flag

   # オフ→オン
   if {$kanji_flag == 0} {
   kinput_start .text over
   set kanji_flag 1

   # オン→オフ
   } else {
   kanjiInput end .text
   set kanji_flag 0
   }
}

# 横位置を記憶しておくかどうかのフラグ mem_y をクリア
proc clear_mem_y { } {
             global mem_y
             set mem_y 0
}

# 編集エントリをクリア
proc clear_edit_entry { } {
          .navi.edit configure -state normal
          .navi.edit  delete 0 end
}

# カーソルを消去
proc cursor_clear { } {
     .text tag remove cur 1.0 end
      lower .cursor
}

# カーソルを置く
proc cursor_put { } {
    .text see insert
    .text tag remove cur 1.0 end
    lower .cursor
    place .cursor -x [expr [lindex [.text bbox insert] 0] \
                                        + [winfo x .text] ] \
                  -y [expr [lindex [.text bbox insert] 1] \
                                        + [winfo y .text] ]
    if {[.text index "insert linestart"] != \
                     [.text index "insert lineend"] } {
        .text tag add cur insert "insert + 1 c"
    } else {
        raise .cursor
    }
}

# バッファ(yanked_text)の中身を調べて表示するプロシージャ
proc research_buffer { } {
   .navi.buffer configure -state normal
   # 空の場合
   if {[string length [.buffer.yanked_text get 1.0 end]] == 1 } {
      .navi.buffer delete 0 end

   # 行の場合
   } elseif {[string compare \
                [.buffer.yanked_text index "end -1 c linestart"] \
                [.buffer.yanked_text index "end -1 c lineend"]] == 0} { 
       set lines_buffer \
            [ expr int([.buffer.yanked_text index end]) - 2 ]
          .navi.buffer delete 0 end
          .navi.buffer insert insert $lines_buffer 
              if { [expr $lines_buffer] == 1 } {
              .navi.buffer insert insert " line" 
              } else {
              .navi.buffer insert insert " lines" 
              }

   # (行と)文字列の場合(＝末尾に改行コード無し)
   } else {

      # set chars_buffer [string length \
      #         [.buffer.yanked_text get 1.0 "end - 1 c "]]
      #    .navi.buffer delete 0 end
      #    .navi.buffer insert insert $chars_buffer 
      #        if { [expr $chars_buffer] == 1 } {
      #        .navi.buffer insert insert " char" 
      #        } else {
      #        .navi.buffer insert insert " chars" 
      #        }

      # まず行数 n を計算
      set n [expr int([.buffer.yanked_text index "end linestart"]) - 1 ]

      # 総文字数を０にセット
      set sum_of_chars 0

      # n 行の長さをそれぞれ数える
      for {set i 1} { $i <= $n } {incr i 1} {
         .buffer.yanked_text mark set insert $i.0
         set sum_of_chars [expr $sum_of_chars + \
            [expr [string trimleft \
                  [string trimleft \
               [.buffer.yanked_text index "insert lineend"] $i ] . ] ] ]
         # 改行コードの分
         set sum_of_chars [ expr $sum_of_chars + 1 ] 
      }
      # 最終行の改行コードの分を抜いておく
      set sum_of_chars [ expr $sum_of_chars - 1 ] 

     .buffer.yanked_text mark set insert end

     .navi.buffer delete 0 end
     .navi.buffer insert insert $sum_of_chars 
        if { [expr $sum_of_chars] == 1 } {
           .navi.buffer insert insert " char" 
        } else {
           .navi.buffer insert insert " chars" 
        }

   } 
   .navi.buffer configure -state disabled
}

##############################################################
#
#                     カーソル移動関係
#
##############################################################

# 挿入カーソルを右に移動
proc insert_right { } {
    global mem_y
    global insert_pos_x

    if {[string compare [ .text index "insert + 1 chars" ] \
        [ .text index "insert lineend " ]] == 0 } { 
         belll
    } elseif { [string compare \
                  [.text index "insert linestart"] \
                  [.text index "insert lineend"]] == 0} {
         belll 
    } else { 
        .text mark set insert "insert + 1 chars "
    }
    set insert_pos_x [ lindex [ .text bbox insert ] 0 ]
    clear_mem_y 
}

# 挿入カーソルを左に移動
proc insert_left { } {
    global mem_y
    global insert_pos_x

    if {[string compare [ .text index "insert" ] \
                  [ .text index "insert linestart " ]] == 0 } { 
         belll
    } else { 
        .text mark set insert "insert - 1 chars "
    } 
    set insert_pos_x [ lindex [ .text bbox insert ] 0 ]
    clear_mem_y 
}

# 挿入カーソルを下に移動
proc insert_down { } {
    global mem_y
    global insert_pos_x insert_pos_y
    global lines image_line

    .text see insert ;# 横スクロールのため

    if {$mem_y == 0} {
           set insert_pos_x [ lindex [ .text bbox insert ] 0 ]
    } 
    set insert_pos_y [ lindex [ .text bbox insert ] 1 ]
    set old_insert_pos_y  $insert_pos_y 

    # ファイルの末尾行だったらベルを鳴らす
    if {[string compare [.text index "insert lineend + 1 c"] \
                        [.text index end] ] == 0 } {
         belll 
    } else {
        if { $insert_pos_y == [expr 14 * ($lines -1 ) + 3 ] } {
            # 画面の下端だったらスクロールする
            .text yview scroll 1 units 
            set old_insert_pos_y  -100 
        } else {
            # 画面の下端じゃなかったら下に移動
            set insert_pos_y  [expr $insert_pos_y + 14 ]
        }
        .text mark set insert @$insert_pos_x,$insert_pos_y

        set mem_y 1
             

        # 行末で，空行じゃなかったら１文字戻す
        if {[string compare [.text index insert] \
                  [.text index "insert lineend"]] == 0 } {
        if {[string compare [.text index "insert linestart"] \
                [.text index "insert lineend"]] != 0 } {
            .text mark set insert "insert -1 c"
        } }
    }
}

# 挿入カーソルを上に移動
proc insert_up { } {
    global mem_y
    global insert_pos_x insert_pos_y
    global lines

    .text see insert ;# 横スクロールのため

    if {$mem_y == 0} {
           set insert_pos_x [ lindex [ .text bbox insert ] 0 ]
    } 
    set insert_pos_y [ lindex [ .text bbox insert ] 1 ]
    set old_insert_pos_y  $insert_pos_y 

    # ファイルの先頭行だったらベルを鳴らす
    if { [.text index "insert linestart"] == 1.0} {
         belll 
    } else {
         if { $insert_pos_y == 3 } {
              # 画面の上端だったらスクロールする
             .text yview scroll -1 units 
         } else {
              # 画面の上端じゃなかったら，上に移動
              set insert_pos_y  [expr $insert_pos_y - 14 ]
         }
        .text mark set insert @$insert_pos_x,$insert_pos_y

         set mem_y 1

         # 行末で，空行じゃなかったら１文字戻す
         if {[string compare [.text index insert] \
                  [.text index "insert lineend"]] == 0 } {
         if {[string compare [.text index "insert linestart"] \
                [.text index "insert lineend"]] != 0 } {
            .text mark set insert "insert -1 c"
         }
      }
   }
}

proc return_on_virtual { } {
    insert_down 
    clear_mem_y 
    .text xview moveto 0.0 

    # 行頭へ
    .text mark set insert "insert linestart"

    # 空白の限り右に進む
    while {[string compare [.text get insert "insert + 1 c"] " "] \
                                                             == 0 } {
       .text mark set insert "insert + 1 c" 
    }
    cursor_put 
}

# "G" によるジャンプ (行指定)
proc jump_by_large_g {} {
    global navi_edit
    global backup_edit_entry

   .navi.edit configure -state normal
    set n [.navi.edit  get ] 
    set n [string trim $n G ] 
    if {[string compare $n "" ] == 0 } { 
         set n [ expr floor([.text index end]) ] 
    } elseif {[string match *dd $n] == 1 } {
         set n [ expr floor([.text index end]) ] 
    } elseif {[string compare $n "$" ] == 0 } { 
         set n [ expr floor([.text index end]) ] 
    } 

   .text mark set insert [expr int(floor($n)) ].0 
    cursor_put

    clear_edit_entry
    set navi_edit $backup_edit_entry

   .navi.edit configure -state disabled
    focus .virtual  
}

# "G" によるジャンプ (いきなり最後の行)
proc jump_by_large_g_from_virtual {} {
    global navi_edit

   .text yview moveto 1.0 
   .text mark set insert "end lineend"
    if {[string compare [.text index "insert linestart"] \
                        [.text index "insert lineend"] ] != 0 } {
         .text mark set insert "insert - 1 c"
    }
    cursor_put
    focus .virtual  
}

# 行頭じゃなく，空行でもなかったら１文字戻す
proc back_one_char_when_escape { } {
    if {[string compare [.text index insert] \
                   [.text index "insert linestart"]] != 0 } {
       if {[string compare [.text index "insert linestart"] \
                   [.text index "insert lineend"]] != 0 } {
                         .text mark set insert "insert -1 c"
       }
    }
}

# スクロールバーによるｙ方向スクロール
proc text_yview args  {
    global insert_pos_x
    global insert_pos_y
    global lines

    eval .text yview $args
    .text mark set insert @$insert_pos_x,$insert_pos_y

    # 行末で，空行じゃなかったら１文字戻す
    if {[string compare [.text index insert] \
                        [.text index "insert lineend"]] == 0 } {
        if {[string compare [.text index "insert linestart"] \
                            [.text index "insert lineend"]] != 0 } {
            .text mark set insert "insert -1 c"
        }
    }
    cursor_put
}

# １ページ前進
proc forward_one_page { } { 
    global insert_pos_x
    global insert_pos_y

    set xandy [ lindex [ .text yview ] 1 ]
   .text yview scroll 1 page
    set new_xandy [ lindex [ .text yview ] 1 ]
 
    # 変化なければベルを鳴らし，カーソルは移動しない
    # 変化あればカーソル移動
    if { $xandy == $new_xandy } { 
        bell
    } else { 
        .text mark set insert @0,3
    }
    cursor_put
}

# １ページ後退
proc backward_one_page { } { 
    global insert_pos_x
    global insert_pos_y
    global lines

    set xandy [ lindex [ .text yview ] 1 ]
   .text yview scroll -1 page
    set new_xandy [ lindex [ .text yview ] 1 ]
 
    # 変化なければベルを鳴らし，カーソルは移動しない
    # 変化あればカーソル移動
    if { $xandy == $new_xandy } { 
        bell        
    } else { 
        .text mark set insert @0,[expr 14 * ($lines -1 ) + 3 ]
    }
    cursor_put
}

# 半ページ前進
proc forward_half_page { } { 
    global insert_pos_x
    global insert_pos_y
    global lines

    set xandy [ lindex [ .text yview ] 1 ]

    set half_page [expr int($lines/2)]
   .text yview scroll $half_page units 

    set new_xandy [ lindex [ .text yview ] 1 ]
 
    # 変化なければベルを鳴らし，カーソルは移動しない
    # 変化あればカーソル移動
    if { $xandy == $new_xandy } { 
        bell
    } else { 
        .text mark set insert @0,$insert_pos_y
    }
    cursor_put
}

# 半ページ後退
proc backward_half_page { } { 
    global insert_pos_x
    global insert_pos_y
    global lines

    set xandy [ lindex [ .text yview ] 1 ]

    set half_page [expr int($lines/2)]
   .text yview scroll -$half_page units 

    set new_xandy [ lindex [ .text yview ] 1 ]
 
    # 変化なければベルを鳴らし，カーソルは移動しない
    # 変化あればカーソル移動
    if { $xandy == $new_xandy } { 
        bell
    } else { 
        .text mark set insert @0,$insert_pos_y
    }
    cursor_put
}

# $ による行の最後への移動
proc goto_lineend_by_dollar { } {
    global insert_pos_x
    .text mark set insert "insert lineend" 

    # 空行じゃなければ１文字戻す
    if {[string compare [.text index "insert linestart"] \
                        [.text index "insert lineend"  ] ] != 0 } {
          .text mark set insert "insert -1 c"
    }
    set insert_pos_x [ lindex [ .text bbox insert ] 0 ]
    cursor_put
}

#######################################################################
#
#   編集関係
#
#   編集関係のコマンドは，リドゥ，アンドゥを統一的に行なうために，
#   どんなコマンドでも以下のような変数を格納するようにする．
#
#   csi             コマンド開始時の index
#   go_csi_dsi      削除位置に移動するコマンド
#   dsi             削除開始位置の index
#   dei             (削除前の)削除終了時の index
#   delete_command  削除コマンド(deiを求める)
#   dchars          削除した文字列
#   go_dsi_pisi     削除開始時からプレ挿入開始の index への移動コマンド
#   pisi            プレ挿入開始 index
#   pichars         プレ挿入文字列
#   piei            プレ挿入終了 index
#   go_piei_isi     プレ挿入から(真の)挿入開始の index への移動コマンド
#   isi             挿入開始 index
#   insert_command  挿入コマンド(icharsを求める)
#   ichars          挿入文字列
#   iei             挿入終了時の index
#   go_iei_cei      挿入終了時からコマンド終了時のindex への移動コマンド
#   cei             コマンド終了時の index
#######################################################################

# 挿入系コマンドでのエスケープの動作
proc escape_on_text { } {
    global command_name
    global isi iei ichars cei
    global insert_command
    global go_iei_cei
    global undo_flag
    global colored

    set iei [.text index insert]
    set ichars [.text get $isi $iei ]
    set insert_command "" ; # insert_command は空にしておく
    set go_iei_cei "back_one_char_when_escape" 
    eval $go_iei_cei
    set cei [.text index insert]

    # .navi.edit におこなったコマンドを表示する
    .navi.edit configure -state normal
      .tmp delete 0 end  
      .tmp insert 0 $ichars  
       set number [.tmp index end]
       set chars "chars"
       if { $number == "0" } { set chars "char" } ; # 0 文字の場合
       if { $number == "1" } { set chars "char" } ; # 1 文字の場合
      .navi.edit delete 0 end
      .navi.edit insert 0 "$command_name $number $chars"
    .navi.edit configure -state disabled

    focus .virtual 
    cursor_put 
    clear_mem_y 
    catch {kanjiInput end .text} ; # kinput2 を終了する
    set undo_flag 1

    # 色付け
    if {$colored=="1"} {
        colorize $isi $iei
    }

    # 入力した文字のチェック
    latex_check
}

# "i" による挿入
proc i_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Inserting...]}
   .navi.edit configure -state disabled

    set command_name "i"
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command { set dei $dsi }
    eval $delete_command
    set dchars ""
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi ""
    set isi  $csi
}

# "I" による挿入
proc large_i_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Inserting...]}
   .navi.edit configure -state disabled

    set command_name "I"
    set csi [.text index insert]
    set go_csi_dsi { .text mark set insert "insert linestart" }
    eval $go_csi_dsi
    set dsi [.text index insert]
    set delete_command { set dei $dsi }
    set dei $dsi
    set dchars ""
    set go_dsi_pisi ""
    set pisi $dsi
    set pichars ""
    set piei $dsi
    set go_piei_isi ""
    set isi  $dsi
}

# "a" による挿入
proc a_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Appending...]}
   .navi.edit configure -state disabled

    set command_name "a"
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command { set dei $dsi }
    eval $delete_command
    set dchars ""
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi { 
        if {[.text index insert] != [.text index "insert lineend"]} {
                       .text mark set insert "insert + 1c" } }
    eval $go_piei_isi
    set isi [.text index insert ]
}

# "A" による挿入
proc large_a_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Appending...]}
   .navi.edit configure -state disabled

    set command_name "a"
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command { set dei $dsi }
    eval $delete_command
    set dchars ""
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi { .text mark set insert "insert lineend" }
    eval $go_piei_isi
    set isi [.text index insert ]
}

# "o" による挿入
proc o_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Inserting...]}
   .navi.edit configure -state disabled

    set command_name "o"
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command { set dei $dsi }
    eval $delete_command
    set dchars ""
    set go_dsi_pisi { .text mark set insert "insert lineend" }
    eval $go_dsi_pisi
    set pisi [.text index insert ]
    set pichars "\n"
   .text insert insert $pichars 
    set piei [.text index insert]
    set go_piei_isi ""
    eval $go_piei_isi
    set isi [.text index insert ]
    .text see insert
}

# "O" による挿入
proc large_o_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Inserting...]}
   .navi.edit configure -state disabled

    set command_name "O"
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command { set dei $dsi }
    eval $delete_command
    set dchars ""
    set go_dsi_pisi { .text mark set insert "insert linestart" }
    eval $go_dsi_pisi
    set pisi [.text index insert ]
    set pichars "\n"
   .text insert insert $pichars 
    set piei [.text index insert]
    set go_piei_isi { .text mark set insert "insert -1 c" }
    eval $go_piei_isi
    set isi [.text index insert ]
}

# "s" による１文字削除＆挿入
proc s_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # 空行だったらベルを鳴らして何もしない
    if {[string compare [.text index "insert linestart"] \
                        [.text index "insert lineend"]] == 0 } {
        belll ; return }

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Inserting...]}
   .navi.edit configure -state disabled

    set command_name "s"
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command { set dei [.text index "$dsi +1c"] }
    eval $delete_command
    set dchars [.text get $dsi $dei]
    .text delete $dsi $dei
    set go_dsi_pisi ""
    set pichars ""
    set pisi $csi
    set piei $csi
    set go_piei_isi ""
    set isi $csi
}

# "S" による１行削除＆挿入
proc large_s_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Inserting...]}
   .navi.edit configure -state disabled

    set command_name "S"
    set csi [.text index insert]
    set go_csi_dsi { .text mark set insert "insert linestart" }
    eval $go_csi_dsi
    set dsi [.text index insert]
    set delete_command { set dei [.text index "$dsi lineend"] }
    eval $delete_command
    set dchars [.text get $dsi $dei]
    .text delete $dsi $dei
    set go_dsi_pisi ""
    set pichars ""
    set pisi $dsi
    set piei $dsi
    set go_piei_isi ""
    set isi $dsi
}

# "C" による行末まで削除＆挿入
proc large_c_on_virtual { } {
    global command_name
    global csi dsi dei dchars pisi pichars piei isi
    global go_csi_dsi go_dsi_pisi delete_command go_piei_isi
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    focus .text 
    cursor_clear

    # .navi.edit にコマンドを表示
    clear_edit_entry
   .navi.edit insert 0 {[Inserting...]}
   .navi.edit configure -state disabled

    set command_name "C"
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command { set dei [.text index "$dsi lineend"] }
    eval $delete_command
    set dchars [.text get $dsi $dei]
    .text delete $dsi $dei
    set go_dsi_pisi ""
    set pichars ""
    set pisi $csi
    set piei $csi
    set go_piei_isi ""
    set isi $csi
}

# "r" による１字上書き
proc replace_one_char { } {
    global insert_replace
    global csi isi
    global go_csi_dsi dsi go_dsi_pisi pisi pichars piei go_piei_isi 
    global insert_command command_name
    global undo_flag
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set dsi $csi 
    set go_csi_dsi ""
    set go_dsi_pisi ""
    set pisi $csi 
    set pichars ""
    set piei $csi 
    set go_piei_isi ""
    set insert_command ""
    set isi $csi
    set command_name "r"

    set insert_replace $csi ; # カーソル位置の監視に使用する変数．
                            ; # 現在の位置を記録しておく．

    # navi.edit エントリの初期化
    clear_edit_entry 
   .navi.edit insert 0 {[Replacing...]}
   .navi.edit configure -state disabled 

    focus .text
    watch_text_one ; # カーソル位置監視プロシージャへ
}

# "r" でカーソルが移動したらすぐさま１文字消去して抜けるプロシージャ
proc watch_text_one { } {
    global insert_replace
    global navi_edit
    global delete_command 
    global dei isi iei
    global dchars ichars
    global go_iei_cei
    global ichars
    global dsi
    global colored

    # インデックス位置が変わったかどうか
    if {[.text index insert] != $insert_replace} {
    # 行ごと変わってしまっているかどうか
    if {[expr int([.text index insert]) - int($insert_replace)] != 0} {

    # 改行により行ごと変わってしまった場合，すぐさま抜ける
        set dchars [.text get insert "insert +1c"]

        # この場合，dei を求めるのがちょっと厄介(dei はすでに無いので)
        # dsi を１文字進めてつくってやる
        set dsi_line [string trimright \
                     [string trimright $dsi 0123456789] .]
        set dsi_char [string trimleft \
                     [string trimleft $dsi 0123456789] .]
        set dei $dsi_line.[expr $dsi_char +1 ]
        set delete_command { set dei [.text index "insert +1c"] }

       .text delete insert "insert +1c" 

        set isi [.text index "insert -1c"]
        set ichars "\n"
        set iei [.text index insert]
        set go_iei_cei ""

        set insert_replace -1.0 ; # これでループを抜けるようになる
        focus .virtual
        set navi_edit "r 1 char"
        cursor_put
        clear_mem_y

        # 色付け
        if {$colored=="1"} {
            colorize $isi $iei
        }
        return


    # 行は変わっていない場合
    } else {
       set char_now  [string trimleft \
               [string trimleft [.text index insert] 0123456789] .]
       set char_prev [string trimleft \
               [string trimleft $insert_replace 0123456789] .]
       set n [expr $char_now - $char_prev]

       # 前方にカーソルが進んだ場合
       if {$n > 0} {
           set delete_command { set dei [.text index "insert +1c"] }
           set dchars [.text get insert "insert +1c"]

          .text delete insert "insert +1c"
           if {$n > 1} {
             .text delete "insert - [expr $n - 1 ] c" insert
              belll } ; # １文字以上の場合，余分は消してベルを鳴らす
          .text mark set insert "insert -1c"
           eval $delete_command

           set isi [.text index insert]
           set ichars [.text get insert "insert +1c"]
           set iei [.text index "insert +1c"]
           set go_iei_cei { .text mark set insert "insert -1c" }

           set insert_replace -1.0 ; # これでループを抜けるようになる
           focus .virtual
           set navi_edit "r 1 char"
           cursor_put
           clear_mem_y

           # 色付け
           if {$colored=="1"} {
               colorize $isi $iei
           }
           return

       # それ以外の場合(どういう場合？)，
       # ベルを鳴らして .navi.edit に "r 0 char" と出す
       } else {
#           set dei [.text index insert]
#           set delete_command { set dei [.text index insert] }
#           set dchars [.text get insert insert]
#           set isi $dei
#           set ichars ""
#           set iei $dei
#           set go_iei_cei ""
#
#           belll
#           set navi_edit "r 0 char"
#           focus .virtual
#          .text mark set insert insert+1char
#           clear_mem_y
#           return
       }
    }
    }
    after 1 {  if {[focus]==".virtual"} { 
               # Escape で抜けてしまった場合
                 set dei [.text index insert]
                 set delete_command { set dei [.text index insert] }
                 set dchars [.text get insert insert]
                 set isi $dei
                 set ichars ""
                 set iei $dei
                 set go_iei_cei ""
                 belll
                 return 
             } 
              watch_text_one  } ; # 自分自身の再起動
}

# "R" による上書き
proc replace_many { } {
    global insert_replace
    global csi isi
    global go_csi_dsi dsi go_dsi_pisi pisi pichars piei go_piei_isi 
    global insert_command command_name
    global undo_flag
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set dsi $csi 
    set go_csi_dsi ""
    set go_dsi_pisi ""
    set pisi $csi 
    set pichars ""
    set piei $csi 
    set go_piei_isi ""
    set insert_command ""
    set isi $csi
    set command_name "R"

    set insert_replace $csi ; # カーソル位置の監視に使用する変数．
                            ; # 現在の位置を記録しておく．
   .replaced_text delete 1.0 end ; #入力と同時に消した文字列の格納に使用

    # navi.edit エントリの初期化
    clear_edit_entry 
   .navi.edit insert 0 {[Replacing...]}
   .navi.edit configure -state disabled 

    cursor_clear
    focus .text 
    watch_text
}

# "R" でカーソルが移動したら消去する
proc watch_text { } {
    global insert_replace
    global delete_command 
    global dei isi iei
    global dchars ichars
    global go_iei_cei
    global ichars
    global csi
    global num_chars ; # リドゥで使うため global にする
     
    # インデックス位置が変わったかどうか
    if {[.text index insert] != $insert_replace} {
    # 行ごと変わってしまっているかどうか
    if {[expr int([.text index insert]) - int($insert_replace)] != 0} {
    # 改行により行ごと変わってしまっている場合
       .text delete "insert linestart" "insert linestart + 1 c" 
        set insert_replace -1.0
        return
    # 行は変わっていない場合
    } else {
        # まず何文字進んだか数える
        set char_now  [string trimleft \
                [string trimleft [.text index insert] 0123456789] .]
        set char_prev [string trimleft \
                [string trimleft $insert_replace 0123456789] .]
        set n [expr $char_now - $char_prev]

        # 前方にカーソルが進んだ場合
        # (進んだ個数分だけ文字を消去し .replaced_text に格納する)
        if {$n > 0} {
            for {set i 1} { $i <= $n } {incr i} {
                # 行末に達したら，ただのinsertモードに移行
                if {[string compare [.text index insert] \
                         [.text index "insert lineend"]]==0} {
                         set insert_replace -1.0
                         return
                }
               .replaced_text insert end \
                      [.text get insert "insert + 1 c"]
               .text delete insert "insert + 1 c"
            }
            set insert_replace [.text index insert]

        # 後方にカーソルが戻った場合
        # (.replaced_text から文字を引っ張りだして戻してやる)
        } else {
           .text insert insert \
                    [.replaced_text get "end - 1 c + $n c" "end - 1 c"]
           .replaced_text delete "end - 1 c + $n c" "end - 1 c"
           .text mark set insert "insert - 1 c"
            set insert_replace [.text index insert]
        }
    }
    }
    after 1 { 
        # エスケープにより終了した際の処理
        if {[focus]==".virtual"} { 
            # まず置換した文字数(num_chars)を数える
           .replaced_entry delete 0 end
           .replaced_entry insert 0 [.replaced_text get 1.0 end]
            set num_chars [.replaced_entry index end]
            set csi_line [string trimright \
                         [string trimright $csi 0123456789] .]
            set csi_char [string trimleft \
                         [string trimleft $csi 0123456789] .]

            set dei $csi_line.[expr $csi_char + $num_chars - 1]
            set delete_command \
                  { set dei [.text index "insert + $num_chars c -1c"] }

            set dchars [.replaced_text get 1.0 "end-1c"]
           # set isi $csi
           # set ichars [.text get $isi "insert +1c"]
           # set iei [.text index insert]
           # set go_iei_cei ""
           # set cei $iei
           # return 
        } watch_text } ; # 自分自身の再起動
}

# "x" による削除
proc delete_by_x { } {

    # 空行じゃなかったら１文字消す
    if {[string compare [.text index "insert linestart"] \
                    [.text index "insert lineend"]] != 0 } {

    focus .garbage ;# スクリプト実行中はイベントを受け付けない
    global after_id
    after cancel $after_id ;# 前回の after をキャンセルする

    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global undo_flag
    global saved_flag
    global colored

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set dei [.text index "insert +1c"]
    set dchars [.text get insert "insert + 1c"]
    # delete_command は以下のように長いのは redo のときにバッファも
    # 更新しなければならないから...
    set delete_command { set dei [.text index "insert +1c"] 
        .buffer.yanked_text delete 1.0 end
        .buffer.yanked_text insert 1.0 [.text get insert "insert +1c"]
        research_buffer }
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi ""
    set isi $csi
    set insert_command ""
    set ichars ""
    set iei $csi
    set go_iei_cei ""

    # バッファにも格納する
    .buffer.yanked_text delete 1.0 end 
    .buffer.yanked_text insert 1.0 $dchars 
    research_buffer

    # ここで１文字消す
    .text delete insert "insert + 1 c"

 
    # 行末で，空行じゃなかったら１文字戻る
    if {[string compare [.text index insert] \
                [.text index "insert lineend"]] == 0 } {
      if {[string compare [.text index "insert linestart"] \
             [.text index "insert lineend"]] != 0 } {
          .text mark set insert "insert -1 c"
          set go_iei_cei { .text mark set insert "insert -1 c" }
      }
    }
 
    set cei [.text index insert]

    .navi.edit configure -state normal
    .navi.edit delete 0 end
    .navi.edit insert end "1x"
    .navi.edit configure -state disabled

    cursor_put

    # 連続 x の最後だったらキャンセルされずに色付けされる
    if {$colored=="1"} {
        set after_id [ after 200 "colorize $isi $isi" ]
    }
    update
    focus .virtual ;# focus を戻す

    } else { 
    belll} ; # 空行だったら何もせずにベルを鳴らす
}

# "X" による左文字削除
proc delete_by_large_x { } {
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global undo_flag
    global saved_flag
    global colored

    # 行の先頭だったら何もしないでベルを鳴らす
    if {[string compare [.text index "insert linestart"] \
                        [.text index insert]] == 0 } {
        belll ; return }

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_csi_dsi { .text mark set insert "insert -1c" }
    eval $go_csi_dsi
    set dsi [.text index insert]
    set dei [.text index "insert +1c"]
    # delete_command は以下のように長いのは redo のときにバッファも
    # 更新しなければならないから...
    set delete_command { set dei [.text index "insert +1c"] 
        .buffer.yanked_text delete 1.0 end
        .buffer.yanked_text insert 1.0 [.text get insert "insert +1c"]
        research_buffer }
    set dchars [.text get insert "insert + 1c"]
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi ""
    set isi $csi
    set insert_command ""
    set ichars ""
    set iei $csi
    set go_iei_cei ""

    # バッファにも格納する
    .buffer.yanked_text delete 1.0 end 
    .buffer.yanked_text insert 1.0 $dchars 
    research_buffer

    # ここで１文字消す
    .text delete insert "insert + 1 c"

    set cei [.text index insert]

    .navi.edit configure -state normal
    .navi.edit delete 0 end
    .navi.edit insert end "1x"
    .navi.edit configure -state disabled

    # 色付け
    if {$colored=="1"} {
        colorize $isi $iei
    }
}


# "x" による削除(多数)
proc delete_many_by_x { } {
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global num_chars ; # リドゥのときに用いる
    global undo_flag
    global saved_flag
    global colored

    global backup_edit_entry

    # 空行じゃなかったら消す
    if {[string compare [.text index "insert linestart"] \
                        [.text index "insert lineend"]] != 0 } {

    set line [expr int([.text index insert])]
    # insert が何文字目かを数える
    set char [string trimleft \
             [string trimleft [.text index insert] $line ] . ]
    # 行末が何文字目かを数える
    set char_e [string trimleft \
               [string trimleft \
               [.text index "insert lineend" ] $line ] . ]
   
    set n [string trimright [.navi.edit get] x] ; # 文字数を獲得
    set n2 [string trimright $n 0123456789]
        # 妙なことをしている場合，無効にする
        if {[string compare $n2 ""] != 0 } {
            belll
            .navi.edit configure -state normal
            .navi.edit delete 0 end
            .navi.edit insert end "$backup_edit_entry"
            .navi.edit configure -state disabled
            focus .virtual
            return
        } 

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi ""
    set isi $csi
    set insert_command ""
    set ichars ""
    set iei $csi
    set go_iei_cei ""

    # n 文字削除できる場合
    if {[expr $char + $n ] <= $char_e } {
        # バッファにも格納
        .buffer.yanked_text delete 1.0 end
        .buffer.yanked_text insert \
                       1.0 [.text get insert "insert + $n c" ]

        # アンドゥ，リドゥ用
        set dei [.text index "insert + $n c"]
        set num_chars $n
        set delete_command \
             { set dei [compute_dei_for_nx_redo $num_chars] }
        set dchars [.text get insert "insert + $n c"]

        # ここで削除
        .text delete insert "insert + $n c" 
 
    # n文字削除できない場合
    } else {
        # バッファにも格納
        .buffer.yanked_text delete 1.0 end
        .buffer.yanked_text insert \
                       1.0 [.text get insert "insert lineend" ]

        # アンドゥ，リドゥ用
        set dei [.text index "insert lineend"]
        set num_chars $n
        set delete_command \
             { set dei [compute_dei_for_nx_redo $num_chars] }
        set dchars [.text get insert "insert lineend "]

        # ここで削除
        .text delete insert "insert lineend"
        belll ; # ベルも鳴らす
    }
    research_buffer
    .navi.edit configure -state normal
    .navi.edit delete 0 end
    .navi.edit insert end $n
    .navi.edit insert end "x"
    .navi.edit configure -state disabled
 
    set go_iei_cei { compute_go_iei_cei_for_nx }
    compute_go_iei_cei_for_nx
    set cei [.text index insert]

    } else { 
         belll
        .navi.edit configure -state normal
        .navi.edit delete 0 end
        .navi.edit insert end "$backup_edit_entry"
        .navi.edit configure -state disabled
    }
    cursor_put
    focus .virtual

    # 色付け
    if {$colored=="1"} {
        colorize $isi $iei
    }
}

# "x" による削除(多数)のリドゥの際に dei を求める
proc compute_dei_for_nx_redo { num_chars } {

    set line [expr int([.text index insert])]
    # insert が何文字目かを数える
    set char [string trimleft \
             [string trimleft [.text index insert] $line ] . ]
    # 行末が何文字目かを数える
    set char_e [string trimleft \
               [string trimleft \
               [.text index "insert lineend" ] $line ] . ]
   
    set n $num_chars ; # 文字数を獲得

    # n 文字削除できる場合
    if {[expr $char + $n ] <= $char_e } {
        set dei [.text index "insert + $n c"]
        .buffer.yanked_text delete 1.0 end
        .buffer.yanked_text insert 1.0 \
                  [.text get insert "insert + $n c"]
        research_buffer 
        return $dei

    # n文字削除できない場合
    } else {
        belll
        set dei [.text index "insert lineend "]
        .buffer.yanked_text delete 1.0 end
        .buffer.yanked_text insert 1.0 \
                  [.text get insert "insert lineend"]
        research_buffer 
        return $dei
    }
}

# "x" による削除(多数)のリドゥの際に行末で空行じゃなかったら１文字戻る
proc compute_go_iei_cei_for_nx { } {

    if {[string compare [.text index insert] \
                 [.text index "insert lineend"]] == 0 } {
       if {[string compare [.text index "insert linestart"] \
              [.text index "insert lineend"]] != 0 } {
           .text mark set insert "insert -1 c" } }
}

# "X" による左文字削除(多数)
proc delete_many_by_large_x { } {
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global num_chars ; # リドゥのときに用いる
    global undo_flag
    global saved_flag
    global colored

    global backup_edit_entry

    # 空行じゃなかったら消す
    if {[string compare [.text index "insert linestart"] \
                        [.text index "insert lineend"]] != 0 } {

    set line [expr int([.text index insert])]
    # insert が何文字目かを数える
    set char [string trimleft \
             [string trimleft [.text index insert] $line ] . ]
    # 文字数を獲得
    set n [string trimright [.navi.edit get] X]
    set n2 [string trimright $n 0123456789]

        # 妙なことをしている場合，無効にする
        if {[string compare $n2 ""] != 0 } {
            belll
            .navi.edit configure -state normal
            .navi.edit delete 0 end
            .navi.edit insert end "$backup_edit_entry"
            .navi.edit configure -state disabled
            focus .virtual
            return
        } 

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi ""
    set isi $csi
    set insert_command ""
    set ichars ""
    set iei $csi
    set go_iei_cei ""

    set go_csi_dsi { go_csi_dsi_for_n_large_x }
    eval $go_csi_dsi
    set dsi [.text index insert]

    # バッファにも格納
#    .buffer.yanked_text delete 1.0 end
#    .buffer.yanked_text insert 1.0 [.text get $dsi $csi ]
    set dei $csi
    set delete_command { set dei $csi }
    set dchars [.text get $dsi $csi ]

    # ここで削除
    .text delete $dsi $csi
 
#    research_buffer
    .navi.edit configure -state normal
    .navi.edit delete 0 end
    .navi.edit insert end $n
    .navi.edit insert end "X"
    .navi.edit configure -state disabled

    } else { 
         belll
        .navi.edit configure -state normal
        .navi.edit delete 0 end
        .navi.edit insert end "$backup_edit_entry"
        .navi.edit configure -state disabled
    }
    cursor_put
    focus .virtual
    # 色付け
    if {$colored=="1"} {
        colorize $isi $iei
    }
}

# nX の際の go_csi_dsi
proc go_csi_dsi_for_n_large_x { } {
    set line [expr int([.text index insert])]

    # insert が何文字目かを数える
    set char [string trimleft \
             [string trimleft [.text index insert] $line ] . ]

    # 文字数を獲得
    set n [string trimright [.navi.edit get] X]
   
    if { $n >= $char } {
        # まずバッファに格納
        .buffer.yanked_text delete 1.0 end
        .buffer.yanked_text insert 1.0 \
                  [.text get "insert linestart" insert]
        research_buffer 
        # それから移動
        .text mark set insert "insert linestart"
    } else {
        # まずバッファに格納
        .buffer.yanked_text delete 1.0 end
        .buffer.yanked_text insert 1.0 \
                  [.text get "insert -$n c" insert]
        research_buffer 
        # それから移動
        .text mark set insert "insert -$n c"
    }
}


# "D" による行末までの削除
proc delete_by_large_d { } {
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global undo_flag
    global saved_flag

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command { set dei [.text index "$dsi lineend"] }
    eval $delete_command
    set dchars [.text get $dsi $dei]
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi ""
    set isi $csi
    set insert_command ""
    set ichars ""
    set iei $csi
    set go_iei_cei ""

    # バッファにも格納する
    .buffer.yanked_text delete 1.0 end 
    .buffer.yanked_text insert 1.0 $dchars 
    research_buffer

    # ここで１文字消す
    .text delete $dsi $dei
 
    # 行末で，空行じゃなかったら１文字戻る
    if {[string compare [.text index "insert linestart"] \
                        [.text index "insert lineend"]] != 0 } {
        .text mark set insert "insert -1 c"
        set go_iei_cei { .text mark set insert "insert -1 c" }
    }
 
    set cei [.text index insert]

    .navi.edit configure -state normal
    .navi.edit delete 0 end
    .navi.edit insert end "D"
    .navi.edit configure -state disabled

    cursor_put
}


# "dd" による行消去
proc delete_by_dd { } { 
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global undo_flag
    global num_chars
    global saved_flag

    .navi.edit  insert insert "d"

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_csi_dsi { .text mark set insert "insert linestart" }
    eval $go_csi_dsi
    set dsi [.text index insert]
                
    # バッファをクリア
    .buffer.yanked_text delete 1.0 end
    .buffer.linenumber delete 1.0 end 

    # 行数を得る
    set n [.navi.edit  get ] 
    set n [string trim $n \n ] 
    set n [string trim $n d ] 
    set num_chars $n

    set delete_command { set dei [compute_dei_for_dd $num_chars] }
    eval $delete_command
    set dchars [.text get insert $dei]
    set go_dsi_pisi ""
    set pisi $dsi
    set pichars ""
    set piei $dsi
    set go_piei_isi ""
    set isi $dsi
    set ichars ""
    set insert_command ""
    set iei $dsi

    # まずバッファに入れる
    .buffer.yanked_text insert end [.text get insert $dei ]
    # そして削除する
    .text delete insert $dei

    # バッファ行に記入
    research_buffer 

    # もし末尾行を消去した場合は１文字戻る
    # ただし空行でない場合のみ
    set go_iei_cei { \
             if {([.text index insert]==[.text index "end -1c"]) && \
                 ([.text index "insert linestart"]!=\
                  [.text index "insert lineend"])} { \
                          .text mark set insert "insert -1c" } }
    eval $go_iei_cei
    set cei [.text index insert]
}

# dd の際に dei を求める
# (ファイル末尾になってしまう場合，n を適当な行数(n_alt)に変更する)
proc compute_dei_for_dd { n } {
    set n_alt [expr [.text index "end linestart"] \
                           - [.text index insert] ] 
    set n_alt [expr int( $n_alt ) ]
    if { $n_alt < $n } { set n $n_alt }

    set index_now [expr int([.text index insert])]
    set dei [expr $index_now + $n].0
    return $dei
}


# "yy" による行ヤンク
proc yank_by_yy { } { 
    global yank_type 
    set yank_type "yy"

   .navi.edit  insert insert "y"

    # 行数nを得る
    set n [.navi.edit  get ] 
    set n [string trim $n \n ] 
    set n [string trim $n y ] 

    # 現在の行を得る
    set linenumber_now [.text index "insert linestart"]

   .buffer.yanked_text delete 1.0 end 
   .buffer.yanked_text insert end \
     [.text get $linenumber_now [expr int($linenumber_now + $n) ].0 ]
            
    research_buffer 
}


# "p" によるプット
proc put_by_p { } {
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global undo_flag
    global saved_flag
    global colored

    global lines_in_yank

    # 空の場合，ベルを鳴らすだけ
    if {[string length [.buffer.yanked_text get 1.0 end]] == 1 } {
        belll
        return
    } else {

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command  { set dei $dsi }
    eval $delete_command
    set dchars ""

    # 行の場合
    if {[string compare \
         [.buffer.yanked_text index "end -1 c linestart"] \
         [.buffer.yanked_text index "end -1 c lineend"]] == 0} { 

        set go_dsi_pisi { .text mark set insert "insert lineend" }
        eval $go_dsi_pisi
        set pisi [.text index insert]
        set pichars "\n"
        .text insert insert "\n"
        set piei [.text index insert]
        set go_piei_isi ""
        set isi $piei
        set insert_command \
            { set ichars [.buffer.yanked_text get 1.0 "end -2c"] }
        eval $insert_command
        .text insert insert $ichars
        set iei [.text index insert]
        set go_iei_cei { .text mark set insert "$csi lineend +1c" }
        eval $go_iei_cei
        set cei [.text index insert]

    # (行と)文字列の場合(＝末尾に改行コード無し)
    } else {

        set go_dsi_pisi ""
        set pisi $csi
        set pichars ""
        set piei $csi
        set go_piei_isi ""

        # 空行でない場合，カーソルを１文字右に
        if {[string compare [.text index "insert linestart"] \
                            [.text index "insert lineend"]] != 0 } {
            set go_piei_isi { .text mark set insert "insert + 1 c" }
            eval $go_piei_isi
        }
        set isi [.text index insert]
        set insert_command \
             { set ichars [.buffer.yanked_text get 1.0 "end -1c"] }
        eval $insert_command
        .text insert insert $ichars
        set iei [.text index insert]
        set go_iei_cei { .text mark set insert "insert -1c" }
        eval $go_iei_cei
        set cei [.text index insert]
    }
    clear_mem_y
    cursor_put
    .navi.edit configure -state normal
    .navi.edit delete 0 end
    .navi.edit insert 0 "put "
    .navi.edit insert end [.navi.buffer get ]
    .navi.edit configure -state disabled
    }

    # 色付け
    if {$colored=="1"} {
        colorize $isi $iei
    }
}


# "P" によるプット
proc put_by_large_p { } {
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global undo_flag
    global saved_flag
    global colored

    global lines_in_yank

    # 空の場合，ベルを鳴らすだけ
    if {[string length [.buffer.yanked_text get 1.0 end]] == 1 } {
        belll
        return
    } else {

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set delete_command  { set dei $dsi }
    eval $delete_command
    set dchars ""

    # 行の場合
    if {[string compare \
               [.buffer.yanked_text index "end -1 c linestart"] \
               [.buffer.yanked_text index "end -1 c lineend"]] == 0} { 

        set go_dsi_pisi { .text mark set insert "insert linestart" }
        eval $go_dsi_pisi
        set pisi [.text index insert]
        set pichars "\n"
        .text insert insert "\n"
        set piei [.text index insert]
        set go_piei_isi {.text mark set insert "insert -1c"}
        eval $go_piei_isi
        set isi [.text index insert]
        set insert_command \
            { set ichars [.buffer.yanked_text get 1.0 "end -2c"] }
        eval $insert_command
        .text insert insert $ichars
        set iei [.text index insert]
        set go_iei_cei { .text mark set insert "$csi linestart" }
        eval $go_iei_cei
        set cei [.text index insert]

    # (行と)文字列の場合(＝末尾に改行コード無し)
    } else {

        set go_dsi_pisi ""
        set pisi $csi
        set pichars ""
        set piei $csi
        set go_piei_isi ""
        set isi $csi
        set insert_command \
             { set ichars [.buffer.yanked_text get 1.0 "end -1c"] }
        eval $insert_command
        .text insert insert $ichars
        set iei [.text index insert]
        set go_iei_cei ""
        set cei $iei
    }
    clear_mem_y
    cursor_put
    .navi.edit configure -state normal
    .navi.edit delete 0 end
    .navi.edit insert 0 "Put "
    .navi.edit insert end [.navi.buffer get ]
    .navi.edit configure -state disabled
    }

    # 色付け
    if {$colored=="1"} {
        colorize $isi $iei
    }
}

# J による連結
proc connect_by_large_j { } {
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global undo_flag
    global saved_flag

    # もしファイル末尾行だったらベルを鳴らすだけでなにもしない
    if {[string compare [.text index "insert lineend"] \
                        [.text index "end - 1 c lineend"] ] == 0 } {
        belll 

    # ファイル末尾行でない場合
    } else { 

        # Saved フラグを寝かせる
        set saved_flag 0

        # [Saved] を [Modified] にする
        filename_modified

        set undo_flag 1
        set csi [.text index insert]
        set go_csi_dsi { .text mark set insert "insert lineend" }
        .text mark set insert "insert lineend"
        set dsi [.text index insert]
        .text mark set mark_dsi insert ; # 削除開始位置に mark
        .text mark set mark_dei "insert +1c" ; # 削除終了位置に mark

        # 空白だったら削除終了位置を１文字ずつ進めていくループ
        while {[string compare \
               [ .text get mark_dei "mark_dei + 1c"] " " ] == 0 } {
                   .text mark set mark_dei "mark_dei +1c" }

        set dei [.text index mark_dei]
        set delete_command { set dei [compute_dei_for_J]}
        set dchars [.text get mark_dsi mark_dei]
        set go_dsi_pisi ""
        set pisi $dsi
        set pichars ""
        set piei $dsi
        set go_piei_isi ""
        set isi $dsi
        set insert_command  { set ichars [compute_ichars_for_J] }
        eval $insert_command


        # ここで削除
        .text delete mark_dsi mark_dei

        # ここで挿入
        .text insert insert $ichars

        set iei [.text index insert]

        set go_iei_cei { if {$ichars == " "} \
              { .text mark set insert "insert -1 c" } else { } }
        eval $go_iei_cei
        set cei [.text index insert] 

        .navi.edit configure -state normal
        .navi.edit delete 0 end
        .navi.edit insert 0 "J"
        .navi.edit configure -state disabled

        clear_mem_y
        cursor_put
   }
}

# J による連結の際に dei を求める
proc compute_dei_for_J { } {
    .text mark set mark_dei "insert +1c" ; # 削除終了位置に mark
    # 空白だったら削除終了位置を１文字ずつ進めていくループ
    while {[string compare \
           [ .text get mark_dei "mark_dei + 1c"] " " ] == 0 } {
               .text mark set mark_dei "mark_dei +1c" }
    set dei [.text index mark_dei]
    return $dei
}

# J による連結の際に ichars を求める(１文字の空白を入れるか否か)
proc compute_ichars_for_J { } {
    if {[.text index "insert linestart"]== \
               [.text index "insert lineend"]} {
        set ichars "" 
    } elseif { [joint_asciis]=="1"} {
        set ichars " "
    } else { 
        set ichars ""
    }
    return $ichars
}

# J による連結の際に空白以外の ascii 文字を接続しようとしているか
# どうかを判断する
proc joint_asciis { } {
    global isi
    set ascii_char {abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-^\\@\[\];:,./!"#$%&'()~=|`\{\}*+<>?_}

    set char1 [.text get "$isi -1c" $isi]
    set char2 [.text get "$isi +1c" "$isi +2c"]
    set flag [expr [regexp "\\$char1" $ascii_char] \
                 * [regexp "\\$char2" $ascii_char] ]
    return $flag
}

proc toplevel_yanked_text { } {
    set x_pointer [winfo pointerx .]
    set y_pointer [winfo pointery .]
    wm geometry .buffer +[expr $x_pointer -350]+[expr $y_pointer -350]
    wm deiconify .buffer 
}

# バッファのポップアップ
proc buffer_popup { } {
    .navi.buffer_label.menu post  \
                    [expr [winfo pointerx . ] - 40 ] \
                    [expr [winfo pointery . ] - 10 ]
    .navi.buffer_label.menu activate 0
      
}

# 履歴バッファのポップアップ
proc history_buffer_popup { } {
    set x [expr [winfo x .navi.buffer_label.menu ] +20 ]
    set y [expr [winfo y .navi.buffer_label.menu ] -390 ]
   .his_buffer post $x $y
   .his_buffer activate 21
}

proc buffer_ok { } {
    research_buffer
    wm withdraw .buffer
}


# マウスによる行ヤンク
proc trace_for_line_yank { } {
    global line_yank
    global backup_edit_entry 
    global yank_line_start 
    global yank_end 

    if { $line_yank > 0.5 } {
       .text mark set insert current
        set yank_line_start [ .text index "insert linestart" ]
        cursor_put
    }
    while { $line_yank > 0.5 } {
       # カーソルが見えるようにスクロール
      .text mark set insert current
      .text mark set insert "insert lineend + 1 c "
      .text see insert 

       # カーソルが見えるようにスクロール
      .text mark set insert current
      .text mark set insert "insert linestart - 1 c "
      .text see insert 

      .text mark set insert current

       # タグを付ける
      .text tag remove cut_and_paste 1.0 end
      .text tag add cut_and_paste $yank_line_start "insert lineend + 1 c "
       set yank_end [ .text index "insert lineend + 1 c " ]
 
       # 行数を.navi.edit に表示する
      .navi.edit configure -state normal
      .navi.edit delete 0 end 
       set number [ expr int($yank_end) - int($yank_line_start) ] 

       if {$number > 0 } {
             .navi.edit insert insert  $number 
       } else { 
             .navi.edit delete 0 end 
       } 

       update 
     }
    .text mark set insert $yank_line_start
     focus .navi.edit
}
##########################################################
# マークによるヤンク関係
##########################################################

# ヤンクの開始
proc start_line_yank { } {
    global yank_line_start 
    global yank_char_start 
    global panel_color

    set yank_char_start [ .text index insert ]

    set yank_line_start [ .text index "insert linestart" ]

   .text tag remove cut_and_paste 1.0 end
   .text tag add cut_and_paste $yank_char_start 

   .cursor configure -bg $panel_color

   .navi.edit configure -state normal
   .navi.edit delete 0 end
   .navi.edit insert insert "1"
   .navi.edit configure -state disabled
}

# タグの追従
proc trace_for_yank { } {
    global \
        yank_line_start \
        yank_char_start \
        yank_end \
        yank_char_or_line

    .navi.edit configure -state normal
    .navi.edit delete 0 end 

   .text tag remove cut_and_paste 1.0 end

  # 文字か行かで場合分け
  if {$yank_char_or_line == 0 } {

  # (1) 文字の場合
   if {[string compare $yank_char_start "none"] == 0 } { } else {
      set yank_end [.text index insert ]

      # タグの大小で場合分けする
      set trimmed_s [string trimleft \
            [string trimleft $yank_char_start 0123456789 ] "." ] 
      set trimmed_e [string trimleft \
            [string trimleft $yank_end 0123456789 ] "." ] 

      # (a) 等しい場合
      if {[string compare $yank_char_start $yank_end] == 0 } {
      .text tag add cut_and_paste $yank_char_start "insert + 1 c "

          # .navi.edit に入れる文字数をカウント
          set number  1 

      # (b) end の方が start より大きい場合(普通)
      } elseif { $trimmed_s < $trimmed_e } { 
      .text tag add cut_and_paste $yank_char_start "insert + 1 c "

          # .navi.edit に入れる文字数をカウント
          set number  [expr $trimmed_e - $trimmed_s + 1 ] 

      # (c) start の方が end より大きい場合(逆)
      } else { \
      .text tag add cut_and_paste insert "$yank_char_start + 1 c " 

          # .navi.edit に入れる文字数をカウント
          set number  [expr $trimmed_s - $trimmed_e + 1 ] 
      }
      .navi.edit insert insert  $number 
      .navi.edit configure -state disabled
   }

  } else {
  # (2) 行の場合
   if {[string compare $yank_line_start "none"] == 0 } { } else {
      set yank_end [.text index "insert linestart"]

      # タグの大小で場合分け
      # (a) 等しい場合
      if {[string compare $yank_line_start $yank_end] == 0 } {
       .text tag add cut_and_paste $yank_line_start "insert lineend + 1 c "
       set number  1 

      # (b) end の方が start より大きい場合(普通)
      } elseif { $yank_line_start < $yank_end } {
       .text tag add cut_and_paste $yank_line_start "insert lineend + 1 c "
       set number [ expr int($yank_end) - int($yank_line_start) + 1 ] 

      # (c) end の方が start より小さい場合(普通と逆)
      } else { 
       .text tag add cut_and_paste \
             "insert linestart" "$yank_line_start lineend + 1 c"
       set number [ expr int($yank_line_start) - int($yank_end) + 1 ] 
      } 
      .navi.edit insert insert  $number 
      .navi.edit configure -state disabled
    }
  }
}

# ヤンク時の x による消去
proc delete_by_x_in_line_yank { } {
    global yank_char_or_line
    global yank_line_start 
    global yank_char_start 
    global fore_color
    global back_color

    global csi dsi dei dchars pisi pichars piei isi ichars iei cei
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei
    global delete_command insert_command
    global num_chars ; # リドゥのときに用いる
    global undo_flag
    global saved_flag

    # 行を指定していたらベルを鳴らしてすぐ抜ける
    if {$yank_char_or_line == 1 } { belll ; return }

    # マークの先頭に insert を移動
    .text mark set insert cut_and_paste.first

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1
    set csi [.text index insert]
    set go_csi_dsi ""
    set dsi $csi
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi ""
    set isi $csi
    set insert_command ""
    set ichars ""
    set iei $csi
    set go_iei_cei ""

    # バッファにも格納
    .buffer.yanked_text delete 1.0 end
    .buffer.yanked_text insert insert \
                 [.text get cut_and_paste.first cut_and_paste.last]
    research_buffer

    # 文字数を数える
    set s [string trimleft [.text index cut_and_paste.first] 0123456789]
    set s [string trimleft $s . ]
    set e [string trimleft [.text index cut_and_paste.last] 0123456789]
    set e [string trimleft $e . ]
    set n [expr $e - $s ]

    # アンドゥ，リドゥ用
    set dei [.text index "insert + $n c"]
    set num_chars $n
    set delete_command \
         { set dei [compute_dei_for_nx_redo $num_chars] }
    set dchars [.text get insert "insert + $n c"]

    # ここで削除
    .text delete insert "insert + $n c" 

    #.text delete cut_and_paste.first cut_and_paste.last

    set go_iei_cei { compute_go_iei_cei_for_nx }
    compute_go_iei_cei_for_nx
    set cei [.text index insert]

    .navi.edit configure -state normal
    .navi.edit insert end "x" 
    .navi.edit configure -state disabled
    focus .virtual
    .text tag configure cur -background $fore_color \
                            -foreground $back_color
    .cursor configure -bg fore_color
    cursor_put 
    set yank_line_start "none"
    set yank_char_start "none"
    set yank_char_or_line 0
}


#######################################################
#                      検索関係
#######################################################

# 状態を戻すプロシージャ
#        無：0 ，↓：1 ， ↑：2
proc navi_search_cond_back { } {
    global navi_search_cond
    global panel_color
    if {$navi_search_cond == 0} {
        .replace.direction configure -text "  "  -relief flat 
        .replace.search configure -bg $panel_color
    } elseif {$navi_search_cond == 1} {
        .replace.direction configure -text "↓"  -relief raised 
    } elseif {$navi_search_cond == 2} {
        .replace.direction configure -text "↑"  -relief raised 
    } 
    catch { kanjiInput end .replace.search }
}

proc search_forward { } {
    global backup_navi_search
    global navi_search
    global search_forward_flag
    global back_color

    catch { lower .pattern_not_found }
    set backup_navi_search $navi_search
    set navi_search ""
    set search_forward_flag 1
    cursor_clear
    .replace.direction configure -text "↓"  -relief raised 
    .replace.search configure -bg $back_color -state normal
    focus .replace.search
}

proc search_backward { } {
    global backup_navi_search
    global navi_search
    global search_forward_flag
    global back_color

    catch { lower .pattern_not_found }
    set backup_navi_search $navi_search
    set navi_search ""
    set search_forward_flag 0
    cursor_clear
    .replace.direction configure -text "↑"  -relief raised 
    .replace.search configure -bg $back_color -state normal
    focus .replace.search
}

proc search_return { } {
    global search_forward_flag
    global navi_search_cond
    if {$search_forward_flag == 1} {
        searching 1
        set navi_search_cond 1
    } else {
        searching 0
        set navi_search_cond 2
    }
    catch { kanjiInput end .replace.search }
    .replace.search configure -state disabled
}

proc searching actual_forward_flag {
    global navi_search
    global backup_navi_search
    global navi_search_cond
    global search_text

    # 前方の場合
    if {$actual_forward_flag == 1 } {
        set another_part 0

        # まず insert + 1 c より後のテキストを検索
        set results \
          [kstring first $navi_search [.text get "insert + 1 c" end] ]

        # 無い場合，テキストの最初から insert (＋α) までを検索
        if { $results == -1 } {
            set results \
           [kstring first $navi_search [.text get 1.0 "insert + 50 c"] ]
            set another_part 1
        }

        # 結果が無しの場合，Pattern not Found を出す
        if { $results == -1 } {
            place .pattern_not_found \
                    -x [expr [winfo x .replace.search] \
                           + [winfo x .replace] + 1] \
                    -y [expr [winfo y .replace.search] \
                           + [winfo y .replace] + 1] 
            update
            raise .pattern_not_found
            belll
            cursor_put
            after 1500 { lower .pattern_not_found }

        # 後半に結果ありの場合
        } elseif { $another_part == 0 } {
            # 該当場所までのテキストを一旦 .search.text に
            # 叩き込んでおいてその最終位置のインデックスを取り出す
            .search_text delete 1.0 end 
            .search_text insert 1.0 [.text get 1.0 "insert + 1 c"]
            .search_text insert end \
             [kstring range [.text get "insert + 1 c" end] 0 $results]
            set search_index [.search_text index "end - 2 c"]
      
            .text mark set insert $search_index
            cursor_put

        # 前半に結果ありの場合
        } else {
            # 該当場所までのテキストを一旦 .search.text に
            # 叩き込んでおいてその最終位置のインデックスを取り出す
            .search_text delete 1.0 end 
            .search_text insert end \
             [kstring range [.text get 1.0 "insert + 50 c"] 0 $results]
            set search_index [.search_text index "end - 2 c"]
      
            .text mark set insert $search_index
            cursor_put
        }

    # 後方の場合
    } else {
        set another_part 0

        # ※後方の場合にのみ，検索文字列の長さを(インデックスで)
        #   知る必要がある
        set length [.replace.search index end]

        # まず "insert ＋長さ" より前のテキストを検索
        set results [kstring last $navi_search \
                    [.text get 1.0 "insert +$length c -1 c" ] ]

        # 無い場合，テキストの最後から insert (＋α) までを検索
        if { $results == -1 } {
            set results \
            [kstring last $navi_search [.text get "insert - 50 c" end] ]
            set another_part 1
        }

        # 結果が無しの場合，Pattern not Found を出す
        if { $results == -1 } {
            place .pattern_not_found \
               -x [expr [winfo x .replace.search] \
                      + [winfo x .replace] + 1] \
               -y [expr [winfo y .replace.search] \
                      + [winfo y .replace] + 1] 
            update
            raise .pattern_not_found
            belll
            cursor_put
            after 1500 { lower .pattern_not_found }

        # 前半に結果ありの場合
        } elseif { $another_part == 0 } {
            # 該当場所までのテキストを一旦 .search.text に
            # 叩き込んでおいてその最終位置のインデックスを取り出す
            .search_text delete 1.0 end 
            .search_text insert end \
             [kstring range \
             [.text get 1.0 "insert + $length c -1 c" ] 0 $results]
            set search_index [.search_text index "end - 2 c"]
      
            .text mark set insert $search_index
            cursor_put

        # 後半に結果ありの場合
        } else {
            # 該当場所までのテキストを一旦 .search.text に
            # 叩き込んでおいてその最終位置のインデックスを取り出す
            .search_text delete 1.0 end 
            .search_text insert end [.text get 1.0 "insert - 50 c" ] 
            .search_text insert end \
             [kstring range [.text get "insert - 50 c" end] 0 $results]
            set search_index [.search_text index "end - 2 c"]
      
            .text mark set insert $search_index
            cursor_put
        }
    }
    focus .virtual
}

proc search_by_small_n { } {
    global search_forward_flag
    if {$search_forward_flag == 1} {
        searching 1
    } else {
        searching 0
    }
}

proc search_by_large_n { } {
    global search_forward_flag
    if {$search_forward_flag == 1} {
        searching 0
    } else {
        searching 1
    }
}

# sed による置換
proc do_replace { } {
    global replace_from replace_to
    global sed_command
    global colored

    global csi go_csi_dsi dsi dei delete_command dchars go_dsi_pisi
    global pisi pichars piei go_piei_isi isi insert_command ichars
    global iei go_iei_cei cei
    global saved_flag undo_flag command_name

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1

    # アンドゥ，リドゥのための部分
    set csi [.text index insert]
    .text mark set insert $replace_from.0
    set command_name "replace"
    set go_csi_dsi { .text mark set insert $replace_from.0 } 
    eval $go_csi_dsi
    set dsi [.text index insert]
    set delete_command { 
            # 該当部分のテキストを切り取ってファイルに保存する
            set f [ open tvi_tmp_replace_org_text.txt w ]
            puts $f [.text get $replace_from.0 "$replace_to.0 lineend"]
            close $f

            # sed コマンドをファイルに書き出す
            set f [ open tvi_tmp_sed_command.txt w ]
            puts -nonewline $f [.replace_window.text get 1.0 end]
            close $f

            # sed 実行
            exec $sed_command -f tvi_tmp_sed_command.txt tvi_tmp_replace_org_text.txt > tvi_tmp_replaced_text.txt

            set dei [.text index "$replace_to.0 lineend +1c"] 
            .buffer.yanked_text delete 1.0 end 
            .buffer.yanked_text insert 1.0 \
               [.text get $replace_from.0 "$replace_to.0 lineend +1c"]
            research_buffer }
    eval $delete_command
    set dchars [.text get $dsi $dei]

    # 当該部分のテキストを切り取る
    .text delete $replace_from.0 "$replace_to.0 lineend +1c"

    set go_dsi_pisi ""
    set pisi $dsi
    set pichars ""
    set piei $dsi
    set go_piei_isi ""
    set isi $dsi
    # 結果を読み込む
            #.text insert $replace_from.0 [read $f 10000000]
    set insert_command \
          { \
            set f [ open tvi_tmp_replaced_text.txt r ]
            set ichars [read $f 10000000]
            close $f 
            #set ichars [string trimright $ichars "\n"] 
           
            exec rm -f tvi_tmp_replace_org_text.txt
            exec rm -f tvi_tmp_replaced_text.txt
            exec rm -f tvi_tmp_sed_command.txt
           }

    set f [ open tvi_tmp_replaced_text.txt r ]
    set ichars [read $f 10000000]
    close $f 
    #set ichars [string trimright $ichars "\n"]

    .text insert insert $ichars
    set iei [.text index insert]
    set go_iei_cei back_one_char_when_escape
    back_one_char_when_escape
    set cei [.text index insert]
    cursor_put

    # .navi.edit におこなったコマンドを表示する
    .navi.edit configure -state normal
    .navi.edit insert end $command_name
    .navi.edit insert end " "
    .navi.edit insert end [.navi.buffer get] 
    .navi.edit configure -state disabled

    # 後処理
    exec rm -f tvi_tmp_replace_org_text.txt
    exec rm -f tvi_tmp_replaced_text.txt
    exec rm -f tvi_tmp_sed_command.txt

    # もし色付けされているなら色付けし直す
    if {$colored=="1"} {
        colorize $replace_from.0 [.text index insert]
    }
}

# ウィンドウのポップアップ
proc replace_popup { } {
    global back_color

    set x [expr [winfo rootx .] + [winfo width .]/2 \
                                 - [winfo reqwidth .replace_window]/2]
    set y [expr [winfo rooty .] + [winfo height .]/2 \
                                 - [winfo reqheight .replace_window]/2]
    wm geometry .replace_window +$x+$y
    wm deiconify .replace_window
    focus .replace_window.text
    # もし sed コマンドの最初が "s//" だったら "s/" の直後にカーソルを
    # 持って行っておく
    if {[.replace_window.text get 1.0 1.3]=="s//"} {
        .replace_window.text mark set insert 1.2
    }
}

######################################################
#      括弧のチェック機能
######################################################

proc checkbrace { open close } {
    .text mark set open_index_start insert
    .text mark set close_index_start insert
    set stop 0
    while {$stop == 0} {
        set tmp [.text search -backwards $open open_index_start 1.0 ]
        if {$tmp == ""} { belll ; return }
        .text mark set open_index $tmp
        set results [.text search -backwards \
                              $close close_index_start open_index ]
        if {$results == ""} { 
            set stop 1 ;# 抜けてタグ付けへ
        } else {
            .text mark set open_index_start  open_index
            .text mark set close_index_start $results
        }
    }
    .text tag add brace [.text index open_index]
    after 700 { .text tag remove brace 1.0 end }
}

##################################################################
#               アンドゥのためのプロシージャ群
##################################################################

# アンドゥメインプロシージャ
#       アンドゥか"アン"アンドゥかを判断する
proc undo_main { } {
    global undo_flag
    global saved_flag
    if { $saved_flag == 1 } { belll ; return }
    if { $undo_flag == 1 } {
        undoing
    } elseif { $undo_flag == 0 } {
        unundoing
    }
}

# アンドゥ
proc undoing { } {
    global undo_flag
    global csi dsi dei dchars pisi piei isi iei cei
    global colored

   .text delete $isi $iei
   .text delete $pisi $piei
   .text insert $dsi $dchars

   .text mark set insert $csi

    # lineend だったら１文字戻る
    if {[.text index insert] == [.text index "insert lineend"]} {
        if {[.text index "insert linestart"] \
                          != [.text index "insert lineend"]} {
            .text mark set insert "insert -1 c"
        }
    }
    cursor_put
    set undo_flag 0

    # 色付け
    if {$colored=="1"} {
        colorize $dsi $dei
    }
}

# "アン"アンドゥ
proc unundoing { } {
    global undo_flag
    global csi dsi dei dchars pisi pichars isi ichars iei cei
    global colored

   .text delete $dsi $dei
   .text insert $pisi $pichars
   .text insert $isi $ichars

   .text mark set insert $csi

    # lineend だったら１文字戻る
    if {[.text index insert] == [.text index "insert lineend"]} {
        if {[.text index "insert linestart"] \
                          != [.text index "insert lineend"]} {
            .text mark set insert "insert -1 c"
        }
    }

    cursor_put
    set undo_flag 1

    # 色付け
    if {$colored=="1"} {
        colorize $isi $iei
    }
}

# リドゥイング
proc redoing { } {
    global csi dsi dei dchars pisi pichars piei isi ichars iei cei 
    global go_csi_dsi go_dsi_pisi go_piei_isi go_iei_cei 
    global delete_command insert_command
    global undo_flag
    global num_chars ; # "R"のときなどに使う
    global colored
    global replace_from replace_to sed_command ;# sed による置換のため

    # もし csi が空ならベルだけ鳴らして抜ける
    if {$csi==""} { belll ; return }

    set csi [.text index insert]     ; # まず今の index を csi に記録
    eval $go_csi_dsi                 ; # csi から dsi に移動
    set dsi [.text index insert]     ; # dsi を記録
    eval $delete_command             ; # delete_command で dei を求める
    set dchars [.text get $dsi $dei] ; # dchars を求める
   .text delete $dsi $dei            ; # dsi から dei を削除
    eval $go_dsi_pisi                ; # dsi から pisi に移動
    set pisi [.text index insert]    ; # pisi を記録
   .text insert insert $pichars      ; # pichars を挿入
    set piei [.text index insert]    ; # piei を記録
    eval $go_piei_isi                ; # piei から isi に移動
    set isi [.text index insert]     ; # isi を記録
    eval $insert_command             ; # insert_commandでicharsを求める
   .text insert insert $ichars       ; # ichars を挿入
    set iei [.text index insert]     ; # iei を記録
    eval $go_iei_cei                 ; # iei から cei に移動
    set cei [.text index insert]     ; # cei を記録
    cursor_put
    set undo_flag 1

    # 色付け
    if {$colored=="1"} {
        colorize $isi $iei
    }
}

###############################################################
#       ファイルの読み込み・保存に関するプロシージャ群
###############################################################
# ファイルの読み込みプロシージャ  
proc loadfile filename {
     global tvi_directory
     global new_filename
     global short_filename
     global operated_text
     global maximum_lines
     global color_lines
     global colored
     global dvifilename
     global replace_from
     global replace_to
   
     catch { set f [open "$filename"] 
           .text insert end [read $f 10000000] 
            close $f }
     # 末尾につく余計な改行コードを消去する
     .text delete "end - 1 c " end

     # 初期状態の設定
     .text mark set insert 1.0
     update
     cursor_put
     # .text tag add cur insert "insert + 1 c "
     focus .virtual

     # 行数の表示
     .text_linenumber configure -state normal

     set number_file [open "$tvi_directory/number" ]

     # 行数を読み込む
     .text_linenumber insert end \
     [read $number_file [expr 7*$maximum_lines] ]

     # 末尾につく余計な改行コードを消去する
     .text_linenumber delete "end - 1 c " end

     .text_linenumber configure -state normal

     # navi.main に行数も表示
     .navi.main configure -state normal
     .navi.main delete 0 end 

     # ディレクトリなどをカットしたファイル名を表示する
     set directory [string last / $filename ]
     set short_filename \
        [string range $filename [expr $directory + 1] end ]

     set linenumber 0
     if { $new_filename == 1 } {
         .navi.main insert insert "`$short_filename'" 
         .navi.main insert insert { [New file]}
     } else {
         .navi.main insert insert "`$short_filename'" 
         .navi.main insert insert { [}
         set linenumber [.text index "end -1 c linestart"]
         set linenumber [expr int($linenumber)]
         .navi.main insert insert  $linenumber

         if { $linenumber == 1 } { 
            .navi.main insert insert { line]} 
         } else {
            .navi.main insert insert { lines]} 
         }
     }
    .navi.main configure -state disabled 
    #.navi.main configure -state normal

    # ウィンドウの枠にファイル名を表示
    wm title . "Tvi: $filename"
    # .navi.edit もクリアする
    #.navi.edit configure -state normal
    #.navi.edit delete 0 end
    #.navi.edit configure -state disabled

    # operated_text もクリアし，undo できないようにする
    set operated_text "none"

    # Xdvi 用のファイル名をつくる
    set dvifilename [string trimright $filename "tex"]
    set dvifilename [string trimright $dvifilename "."]

    # 検索／置換のため，最初と最後の行数を設定する
    set replace_from 1
    set replace_to $linenumber

    # 行数が color_lines 以下なら色付けする
    set colored 0
    if {$linenumber <= $color_lines} {
         set colored 1
         colorize 1.0 end
    } else {
         colorize_formula 1.0 end
    }
    
    # しおりのクリア
    del_bookmark
}

# GUIによるファイルの読み込み
proc openfile { } {
    global filename
    global saved_flag
    global new_filename
    global file_dir
    global new_or_insert
    global insert_line
    global kanji_code
    global tvi_directory

    # もし挿入モードだったら escaping する
    if {[focus] == ".text" } {  
       for_redo_end
      .text tag remove inserted_text_tag 1.0 end
       escaping
       clear_mem_y
       catch {kanjiInput end .text}
       for_unundo i
    }

    # 挿入の場合のための挿入位置を現在のカーソルの位置にする
    set insert_line [expr int([.text index "insert linestart"])] 

    #set new_or_insert 0 ; # 読み込みタイプを毎回「新規」にする

    # tk_getOpenFile を呼び出す
    set types {
	{"TeX files"		{.tex .sty}}
	{"Text files"		.txt}
	{"All files"		*}
    }
    set tmp [tk_getOpenFile -filetypes $types -initialdir $file_dir ]

    # Cancel で戻って来た場合，何もしない    
    if {$tmp == ""} {
       return

    # 挿入の場合
    } elseif {$new_or_insert == 1 } {

        # まず insert を動かす
        #.text mark set insert [expr $insert_line + 1 ].0
        .text mark set insert $insert_line.0

        global csi go_csi_dsi dsi dei delete_command dchars go_dsi_pisi
        global pisi pichars piei go_piei_isi isi insert_command ichars
        global iei go_iei_cei cei
        global saved_flag undo_flag command_name
        global colored

        # Saved フラグを寝かせる
        set saved_flag 0

        # [Saved] を [Modified] にする
        filename_modified

        set undo_flag 1

        set command_name "o"
        set csi [.text index insert]
        set go_csi_dsi ""
        set dsi $csi
        set dei $csi
        set delete_command ""
        set dchars ""
        set go_dsi_pisi { .text mark set insert "insert lineend" }
        eval $go_dsi_pisi
        set pisi [.text index insert ]
        set pichars "\n"
        .text insert insert $pichars
        set piei [.text index insert]
        set go_piei_isi ""
        set isi $piei

        # 読み込み
        set f [open "$tmp"]
        set f1 [read -nonewline $f ] 
        .text insert insert $f1
        close $f 

        set iei [.text index insert] 
        set ichars [.text get $isi $iei ]
        set insert_command "" ; # insert_command は空にしておく
        set go_iei_cei "back_one_char_when_escape" 
        eval $go_iei_cei
        set cei [.text index insert] 

        # .navi.edit におこなったコマンドを表示する
        .navi.edit configure -state normal
        .tmp delete 0 end   
        .tmp insert 0 $ichars  
        set number [.tmp index end]
        set chars "chars" 
        if { $number == "0" } { set chars "char" } ; # 0 文字の場合
        if { $number == "1" } { set chars "char" } ; # 1 文字の場合
        .navi.edit delete 0 end
        .navi.edit insert 0 "$command_name $number $chars" 
        .navi.edit configure -state disabled

#        clear_mem_y 
 
        # カーソルを読み込み始めの位置＋１行の位置へ持って行く
       .text mark set insert [expr $insert_line + 1 ].0
        cursor_put

        # 色付け
        if {$colored=="1"} {
            colorize $isi $iei
        } else {
            colorize_formula $isi $iei
        }

    # 新規の場合
    } else {
       # セーブ済みの場合
       if {$saved_flag == 1} {
          .text delete 1.0 end
          .text_linenumber delete 1.0 end
           set new_filename 0
           set filename $tmp
           loadfile $filename ; # 読み込み

           # file_dir の更新
           set fullpathlist [split $filename / ]
           set file_dir [lrange $fullpathlist 0 [expr [llength $fullpathlist ] - 2 ]]
           set file_dir [join $file_dir / ]

           cd $file_dir

       # セーブしていない場合
       } else {
           set verify [tk_dialog .d "verify" \
             "ファイルが保存されていません！" \
              warning 0 "保存してから読み込み" \
                        "保存せず読み込み" \
                        "キャンセル" ]

          # 保存してから読み込み
          if {$verify == 0} {
             # 保存
             set f [ open $filename w ]
             puts -nonewline $f [.text get 1.0 end ]
             close $f

             # 消去
            .text delete 1.0 end
            .text_linenumber delete 1.0 end

             # 読み込み
             set filename $tmp
             set new_filename 0
             loadfile $filename
             set saved_flag 1

             # file_dir の更新
             set fullpathlist [split $filename / ]
             set file_dir [lrange $fullpathlist 0 [expr [llength $fullpathlist ] - 2 ]]
             set file_dir [join $file_dir / ]

             cd $file_dir

          # 保存しないで読み込み
          } elseif {$verify == 1} {
             # 消去
            .text delete 1.0 end
            .text_linenumber delete 1.0 end

             # 読み込み
             set filename $tmp
             set new_filename 0
             loadfile $filename
             set saved_flag 1

             # file_dir の更新
             set fullpathlist [split $filename / ]
             set file_dir [lrange $fullpathlist 0 \
                             [expr [llength $fullpathlist ] - 2 ]]
             set file_dir [join $file_dir / ]
             cd $file_dir

          # それ以外
          } else { return } 
       }
    }
}

# GUI によるファイルのセーブ
proc savefile { } { 
    global file_dir filename short_filename 
    global kanji_code tmp_kanji_code
    global dvifilename

    set tmp_kanji_code $kanji_code

    # もし挿入モードだったら escaping する
    if {[focus] == ".text" } {  
       for_redo_end
      .text tag remove inserted_text_tag 1.0 end
       escaping
       clear_mem_y
       catch {kanjiInput end .text}
       for_unundo i
    }

    # tk_getSaveFile の呼び出し
    set types {
	{"TeX files"		{.tex .sty}}
	{"Text files"		.txt}
	{"All files"		*}
    }
    set tmp [tk_getSaveFile -filetypes $types \
                   -initialdir $file_dir -initialfile $short_filename ]

    # キャンセルで戻って来た場合は何もしない
    if {$tmp == ""} {
       return

    # 編集時と同じ名前に保存する場合
    # (セーブして .navi.main も書き換える)
    } elseif {$tmp == $filename } {
       save_file $filename


    # 編集時とは違う名前だが，存在しない名前の場合
    # (セーブして .navi.main も変更し，ディレクトリも移動)
    } elseif {[file exists $tmp] == 0} {

       # まずセーブする
       set filename $tmp
       save_file $filename

       # Xdvi 用のファイル名をつくる
       set dvifilename [string trimright $filename "tex"]
       set dvifilename [string trimright $dvifilename "."]

       # file_dir を更新する
       set fullpathlist [split $filename / ]
       set file_dir [lrange $fullpathlist 0 \
                         [expr [llength $fullpathlist ] - 2 ]]
       set file_dir [join $file_dir / ]
       cd $file_dir ; # ディレクトリを移動する

    # 編集時とは違う名前で，存在する場合
    # (確認してからセーブし，ディレクトリも移動)
    } else {
       set verify [tk_dialog .d "verify" \
             "$tmp はすでに存在するファイルです．" \
                 warning 1 "上書きして保存" "キャンセル" ]
       if {$verify == 0} { 
           # まずセーブする
           set filename $tmp
           save_file $filename

           # Xdvi 用のファイル名をつくる
           set dvifilename [string trimright $filename "tex"]
           set dvifilename [string trimright $dvifilename "."]

           # file_dir を更新する
           set fullpathlist [split $filename / ]
           set file_dir [lrange $fullpathlist 0 \
                         [expr [llength $fullpathlist ] - 2 ]]
           set file_dir [join $file_dir / ]
           cd $file_dir ; # ディレクトリを移動する
       }
    }
}

# ファイルの上書きセーブ
proc save_file { filename } { 
    global saved_flag
    global short_filename
    global tmp_kanji_code
 
    kanji defaultOutputCode $tmp_kanji_code
    set f [ open $filename w ]
    puts -nonewline $f [.text get 1.0 end ]
    close $f
    set saved_flag 1
 
    # .navi.main に表示する short_filename を作成する
    set directory [string last / $filename ]
    set short_filename \
       [string range $filename [expr $directory + 1] end ]
 
    # navi.main に行数も表示する
    .navi.main configure -state normal
    .navi.main delete 0 end 
    .navi.main insert insert "`$short_filename'" 
    .navi.main insert insert { [}
    set linenumber [.text index "end -1c"]
    set linenumber [expr int($linenumber)]
    .navi.main insert insert $linenumber
    if { $linenumber == 1 } { 
       .navi.main insert insert { line]} 
    } else {
       .navi.main insert insert { lines]} 
    }
    .navi.main configure -state disabled 

    # ウィンドウの枠にファイル名を表示
    wm title . "Tvi: $filename"

}

# .navi.main で space キーが押されたときの動作
proc space_navi_main { } {
    global navi_main_backup
    global new_or_insert
    set navi_char [.navi.main get] 
    if {$navi_char==":e"} {
        .navi.main delete 0 end 
        .navi.main insert 0 $navi_main_backup
        .navi.main configure -state disabled
        focus .virtual
        cursor_put
        set new_or_insert 0 ; openfile
    } elseif {$navi_char==":r"} {
        .navi.main delete 0 end 
        .navi.main insert 0 $navi_main_backup
        .navi.main configure -state disabled
        focus .virtual
        cursor_put
        set new_or_insert 1 ; openfile
    } elseif {$navi_char==":w"} {
        .navi.main delete 0 end 
        .navi.main insert 0 $navi_main_backup
        .navi.main configure -state disabled
        focus .virtual
        cursor_put
        savefile
    }
}

# .navi.main で Return キーが押されたときの動作
proc do_navi_main { } {
    global navi_main
    global saved_flag
    global filename 
    global short_filename 

    # 保存のみ (:w)
    if { [string compare $navi_main ":w" ] == 0 } {
          set f [ open $filename w ]
          puts -nonewline $f [.text get 1.0 end ]
          close $f
          set saved_flag 1

    # navi.main に行数も表示
    .navi.main delete 0 end 
    .navi.main insert insert "`$short_filename'" 
    .navi.main insert insert { [}
    set linenumber [.text index "end -1c"]
    set linenumber [expr int($linenumber)]
    .navi.main insert insert $linenumber

    if { $linenumber == 1 } { 
        .navi.main insert insert { line]} 
    } else {
        .navi.main insert insert { lines]} 
    }
    .navi.main configure -state disabled 

    focus .virtual
    cursor_put

    # 保存して終了 (:wq)
    } elseif { [string compare $navi_main ":wq" ] == 0 } {
          set f [ open $filename w ]
          puts -nonewline $f [.text get 1.0 end ]
          close $f
          catch { eval exec rm -f [glob tvi_*.ppm]  }
          exit

    # 確認後終了 (:q)
    } elseif { [string compare $navi_main ":q" ] == 0 } {
         if { $saved_flag == 1 } {
            catch { eval exec rm -f [glob tvi_*.ppm]  }
            exit
         } else { 
           .navi.main delete 0 end 
           .navi.main insert insert "`$short_filename'" 
           .navi.main insert insert { [Modified]} 
           .navi.main configure -state disabled 
            belll 
            focus .virtual
            cursor_put
         }

    # 強制終了 (:q!)
    } elseif { [string compare $navi_main ":q!" ] == 0 } {
         catch { eval exec rm -f [glob tvi_*.ppm]  }
         exit

    # Return のみ
    } elseif { [string compare $navi_main ":" ] == 0 } {
         .navi.main delete 0 end 
          focus .virtual
          cursor_put
    # それ以外はベルを鳴らして復帰
    } else { 
          belll 
         .navi.main delete 0 end 
          focus .virtual
          cursor_put
    }
} 

# :w[Enter] で保存する
proc save_colon_w { } {
    global navi_main
    global saved_flag
    global filename 
    global short_filename 

    set f [ open $filename w ]
    puts -nonewline $f [.text get 1.0 end ]
    close $f
    set saved_flag 1

    # navi.main に行数も表示
    .navi.main configure -state normal
    .navi.main delete 0 end 
    .navi.main insert insert "`$short_filename'" 
    .navi.main insert insert { [}
    set linenumber [.text index "end -1c"]
    set linenumber [expr int($linenumber)]
    .navi.main insert insert $linenumber

    if { $linenumber == 1 } { 
        .navi.main insert insert { line]} 
    } else {
        .navi.main insert insert { lines]} 
    }
    .navi.main configure -state disabled 

    focus .virtual
    cursor_put
}

proc filename_modified { } {
      global short_filename
         .navi.main configure -state normal
         .navi.main delete 0 end 
         .navi.main insert insert "`$short_filename'" 
         .navi.main insert insert { [Modified]} 
         .navi.main configure -state disabled 
}


########################################################
#      しおりをはさむ／参照する に関するプロシージャ   #
########################################################

set bookmark_number 1

menu .bookmark -tearoff False
.bookmark add command -label "しおりをはさむ" -command add_bookmark

.bookmark add separator
.bookmark add command -label "キャンセル"
.bookmark add separator
.bookmark add separator

proc bookmark_popup { } {
        global bookmark_number
       .bookmark post \
            [expr [winfo rootx .menubuttons_bookmark] + 0] \
            [expr [winfo rooty .menubuttons_bookmark] + 25]

       if {$bookmark_number != 1} {
           for {set i 1} { $i < $bookmark_number } {incr i} {
               set insert [.text index bookmark$i ]
               set line   [string trimright $insert 0123456789 ]
               set line   [string trimright $line   . ]
               set column [ expr 1 + \
                       [string length [.text get \
                        "bookmark$i linestart" bookmark$i ]]]
              .bookmark entryconfigure [expr $i +4 ] \
                         -label "$line 行目 $column 文字目" 
           }
       }
}

proc add_bookmark { } {
    global bookmark_number

    .text mark set bookmark$bookmark_number insert
    .bookmark add command -command "go_bookmark $bookmark_number"

    set bookmark_number [expr $bookmark_number + 1 ]
}

proc go_bookmark { bookmark_number } {
    .text mark set insert [.text index bookmark$bookmark_number]
    .text see insert
    clear_mem_y
    cursor_put
}

proc del_bookmark { } {
    global bookmark_number
    .bookmark delete 4 end
    set bookmark_number 1
}

############################
#  スペルチェック
############################
proc spellcheck { } {
    global tvi_directory filename short_filename ispell_dic
    global saved_flag ispell_id
    global sed_command

    # まず前のタグをすべて消去する
    for {set i 1} {$i<=$ispell_id} {incr i} { 
        .text tag remove bad_spell.$i 1.0 end
    }
    set ispell_id 0
   
    .text configure -cursor watch
    update

    # まず保存
    set f [ open $filename w ]
    puts -nonewline $f [.text get 1.0 end ]
    close $f
    set saved_flag 1

    # navi.main に行数も表示
    .navi.main configure -state normal
    .navi.main delete 0 end 
    .navi.main insert insert "`$short_filename'" 
    .navi.main insert insert { [}
    set linenumber [.text index "end -1 c linestart"]
    set linenumber [expr int($linenumber)]
    .navi.main insert insert  $linenumber

    if { $linenumber == 1 } {
       .navi.main insert insert { line]}
    } else {
       .navi.main insert insert { lines]}
    }
    .navi.main configure -state disabled

    update

    exec $sed_command -f $tvi_directory/sed_commands $filename | ispell  -a -n | grep -v \* | grep -v @ > tvi_tmp_spell_check.txt 

    # ispell 用の辞書ファイルの作成
    set current_dir [ pwd ] ;# まず current directory を保存して
    cd                      ;# cd でホームディレクトリへ(^^;)
    exec touch $ispell_dic  ;# デフォルト個人辞書に touch
    exec cp $ispell_dic $current_dir/tvi_tmp_ispell_dic.txt
    cd $current_dir         ;# また current directory に戻る
    exec cat $tvi_directory/latex_commands     >> tvi_tmp_ispell_dic.txt
    exec cat $tvi_directory/latex_environments >> tvi_tmp_ispell_dic.txt
    exec cat $tvi_directory/latex_misc         >> tvi_tmp_ispell_dic.txt

    # ispell によるスペルチェック
    # ただし，うまく通るようにちょいとフィルターをかます
    exec $sed_command -f $tvi_directory/sed_commands $filename | ispell  -a -n -p tvi_tmp_ispell_dic.txt | grep -v \* | grep -v @ > tvi_tmp_spell_check.txt.org 
    # 後のためにメッセージも加工しておく( : を , に)
    exec $sed_command s/:/,/g tvi_tmp_spell_check.txt.org > tvi_tmp_spell_check.txt
    set file [open tvi_tmp_spell_check.txt]
    .spell_text delete 1.0 end
    .spell_text insert end [read $file 10000000 ]
    exec rm -f tvi_tmp_spell_check.txt
    exec rm -f tvi_tmp_spell_check.txt.org
    exec rm -f tvi_tmp_ispell_dic.txt

    for { set i 1 } { $i <= 10000000 } { incr i } {
        set top [.text get $i.0 "$i.0  + 1 c"]
        if { $top == "" } {
           break
        }
           set i_spell [ expr $i ]
           set flag [.spell_text get "${i_spell}.0 linestart" \
                                     "${i_spell}.0 lineend"     ]
           if {$flag == ""} {
           } elseif {[lindex $flag 0] == "#" } {
               spell_tag $i $flag
              .spell_text delete "${i_spell}.0 linestart" \
                                 "${i_spell}.0 lineend + 1 c" 
               set i [ expr $i -1 ]
           } elseif {[lindex $flag 0] == "&" } {
               spell_tag $i $flag
               .spell_text delete "${i_spell}.0 linestart" \
                                "${i_spell}.0 lineend + 1 c" 
               set i [ expr $i -1 ]
           } elseif {[lindex $flag 0] == "?" } {
               spell_tag $i $flag
               .spell_text delete "${i_spell}.0 linestart" \
                                "${i_spell}.0 lineend + 1 c" 
               set i [ expr $i -1 ]
           } elseif {[lindex $flag 0] == "+" } {
               .spell_text delete "${i_spell}.0 linestart" \
                                "${i_spell}.0 lineend + 1 c" 
               set i [ expr $i -1 ]
           } elseif {[lindex $flag 0] == "-" } {
               .spell_text delete "${i_spell}.0 linestart" \
                                "${i_spell}.0 lineend + 1 c" 
               set i [ expr $i -1 ]
           } else {
           }
    }
    .text configure -cursor xterm
}

# ispell で引っかかった箇所にタグを付けて色づけする
proc spell_tag { i flag } {
    global ispell_id
    global ispell_errors
    global fore_color

    set line [.text get "$i.0 linestart" "$i.0 lineend"]
    set flag_top [lindex $flag 0]
    if {$flag_top == "#"} {
        set line [ string range $line 0 [lindex $flag 2] ]
    } elseif {$flag_top == "&"} {
        set offset [lindex $flag 3]
        set offset [string trimright $offset , ]
        set line [ string range $line 0 $offset ]
    } elseif {$flag_top == "?"} {
        set offset [lindex $flag 3]
        set offset [string trimright $offset , ]
        set line [ string range $line 0 $offset ]
    } else { }
   .spell_tmp delete 0 end 
   .spell_tmp insert end $line 
    set index [expr [ .spell_tmp index end ] - 1 ]
    set length [ string length [lindex $flag 1] ]

    incr ispell_id
    # タグ付け
    .text tag add bad_spell.$ispell_id  $i.$index \
                                        $i.[expr $index + $length]
    # タグの設定
    .text tag configure bad_spell.$ispell_id \
                          -background Red -foreground $fore_color
    .text tag bind bad_spell.$ispell_id <Enter> \
                            { .text configure -cursor top_left_arrow }
    .text tag bind bad_spell.$ispell_id <Leave> \
                            { .text configure -cursor xterm } 
    .text tag bind bad_spell.$ispell_id <ButtonPress> \
                            { popup_ispell }
    .text tag lower bad_spell.$ispell_id cur

    # エラーメッセージを global なリストに格納する
    set ispell_errors($ispell_id) $flag
}


# スペルチェック時のポップアップ
proc popup_ispell { } {
    global ispell_errors
    global ispell_current_tag

    # まずマウスポインタの位置の index を得る
    set pointerx [expr [winfo pointerx .text]-[winfo rootx .text]]
    set pointery [expr [winfo pointery .text]-[winfo rooty .text]]
    set current_index [.text index @$pointerx,$pointery] 
    # タグ名を得る
    set ltags [.text tag names $current_index]
    set ispell_current_tag \
            [lindex $ltags [lsearch -glob $ltags bad_spell* ] ]
    set ispell_id [string trimleft $ispell_current_tag bad_spell]
    set ispell_id [string trimleft $ispell_id .]
    # , でリストにし直す
    set error_msg [split $ispell_errors($ispell_id) , ]
    # タグの index を得る
    set tag_first [.text index $ispell_current_tag.first]
    set tag_last  [.text index $ispell_current_tag.last]

    # メニューの作成
    .ispell delete 11 end
    .ispell add separator

    set flag_top [lindex [lindex $error_msg 0] 0]

    if { $flag_top=="&" | $flag_top=="?" } {
        set start 1
        set end [llength $error_msg]
        for {set i $start} {$i<$end} {incr i} { 
            .ispell add command -label "[lindex $error_msg $i] にこの回だけ置き換える" \
            -command "ispell_replace [subst $tag_first] [subst $tag_last] { [subst [lindex $error_msg $i] ] }"
            .ispell add command -label "[lindex $error_msg $i] にすべての同じ単語を置き換える" \
            -command "ispell_replace_all [subst $tag_first] [subst $tag_last] { [subst [lindex $error_msg $i] ] }"
            .ispell add separator
        }
    }
    update
    .ispell post [expr [winfo pointerx .text] +30] \
                 [expr [winfo pointery .text] +10] 
}

# ispell 提示の候補への置換え(この回だけ)
proc ispell_replace { tag_first tag_last replace_word } {
   
    global csi go_csi_dsi dsi dei delete_command dchars go_dsi_pisi
    global pisi pichars piei go_piei_isi isi insert_command ichars
    global iei go_iei_cei cei
    global saved_flag undo_flag command_name

    # Saved フラグを寝かせる
    set saved_flag 0

    # [Saved] を [Modified] にする
    filename_modified

    set undo_flag 1

    .text mark set insert $tag_first
    .text see insert ;# 念のため
    set command_name "cw"
    set csi $tag_first
    set go_csi_dsi ""
    set dsi $csi
    set dei $tag_last
    set delete_command { set dei [.text index "insert wordend"] 
        .buffer.yanked_text delete 1.0 end 
        .buffer.yanked_text insert 1.0 \
                [.text get insert [.text index "insert wordend"] ]
        research_buffer }
    set dchars [.text get $tag_first $tag_last]
    .buffer.yanked_text delete 1.0 end 
    .buffer.yanked_text insert 1.0 [.text get $tag_first $tag_last]
    research_buffer 
    .text delete $tag_first $tag_last ;# ここで削除
    set go_dsi_pisi ""
    set pisi $csi
    set pichars ""
    set piei $csi
    set go_piei_isi ""
    set isi $csi
    set insert_command ""
    set ichars [string trim $replace_word]
    .text insert $tag_first $ichars ;# ここで挿入
    set iei [.text index insert]
    set go_iei_cei back_one_char_when_escape
    back_one_char_when_escape
    set cei [.text index insert]
    cursor_put

    # .navi.edit におこなったコマンドを表示する
    .navi.edit configure -state normal
    .tmp delete 0 end   
    .tmp insert 0 $ichars  
    set number [.tmp index end]
    set chars "chars" 
    if { $number == "0" } { set chars "char" } ; # 0 文字の場合
    if { $number == "1" } { set chars "char" } ; # 1 文字の場合
    .navi.edit delete 0 end
    .navi.edit insert 0 "$command_name $number $chars" 
    .navi.edit configure -state disabled
}

# ispell 提示の候補への置換え(すべての同じ単語)
proc ispell_replace_all { tag_first tag_last replace_word } {
    global ispell_current_tag
    global ispell_id
    set current_index [.text index insert]
    set tag_word \
      [.text get $ispell_current_tag.first $ispell_current_tag.last ]

    for {set num 1} { $num <= $ispell_id } {incr num} {
        catch { set tag_word_num \
            [.text get bad_spell.$num.first bad_spell.$num.last] }
        if {$tag_word_num==$tag_word} {
            ispell_replace [.text index bad_spell.$num.first] \
                           [.text index bad_spell.$num.last] \
                           $replace_word
        }
    }
    .text mark set insert $current_index
    cursor_put
}

# ispell で「この回だけOK」
proc ispell_current_ok { } {
    global ispell_current_tag
    .text tag remove $ispell_current_tag 1.0 end
}

# ispell で「同じ単語はすべてOK」
proc ispell_same_word_ok { } {
    global ispell_current_tag
    global ispell_id
    set tag_word \
      [.text get $ispell_current_tag.first $ispell_current_tag.last ]

    for {set num 1} { $num <= $ispell_id } {incr num} {
        catch { set tag_word_num \
            [.text get bad_spell.$num.first bad_spell.$num.last] }
        if {$tag_word_num==$tag_word} {
            .text tag remove bad_spell.$num 1.0 end 
        }
    }
}

# ispell で「すべての単語がOK」
proc ispell_all_ok { } {
    global ispell_id

    for {set num 1} { $num <= $ispell_id } {incr num} {
        .text tag remove bad_spell.$num 1.0 end 
    }
}

# ispell で「個人辞書に登録して同じ単語のチェックも外す」
proc ispell_regist_dic_following_ok { } {
    global ispell_current_tag
    global ispell_id
    global ispell_dic
    set current_dir [ pwd ] ;# まず current directory を保存して
    cd                      ;# cd でホームディレクトリへ
    exec touch tvi_ispell_tmp.txt
    set f [open tvi_ispell_tmp.txt w ]
    puts $f [.text get $ispell_current_tag.first \
                       $ispell_current_tag.last ]
    close $f
    exec cp $ispell_dic $ispell_dic.old ;# バックアップ
    exec cat tvi_ispell_tmp.txt >> $ispell_dic
    exec rm tvi_ispell_tmp.txt
    cd $current_dir         ;# また current directory に戻る

    set tag_word \
      [.text get $ispell_current_tag.first $ispell_current_tag.last ]

    for {set num 1} { $num <= $ispell_id } {incr num} {
        catch { set tag_word_num \
            [.text get bad_spell.$num.first bad_spell.$num.last] }
        if {$tag_word_num==$tag_word} {
            .text tag remove bad_spell.$num 1.0 end 
        }
    }
}

# ispell で「個人辞書に登録するが同じ単語のチェックは外さない」
proc ispell_regist_dic { } {
    global ispell_current_tag
    global ispell_dic
    set current_dir [ pwd ] ;# まず current directory を保存して
    cd                      ;# cd でホームディレクトリへ
    exec touch tvi_ispell_tmp.txt
    set f [open tvi_ispell_tmp.txt w ]
    puts $f [.text get $ispell_current_tag.first \
                       $ispell_current_tag.last ]
    close $f
    exec cat tvi_ispell_tmp.txt >> $ispell_dic
    exec rm tvi_ispell_tmp.txt
    cd $current_dir         ;# また current directory に戻る

    .text tag remove $ispell_current_tag 1.0 end
}

###################################
#        その他もろもろ           #
###################################
proc terminal_popup {  } {
    global terminal_command
    eval exec $terminal_command &
}

proc help_popup {  } {
    global help_command
    global tvi_directory 
    after 1 { eval exec $help_command \
                      ${tvi_directory}/Tvi_help_index.html & }
}

# 行番号を自動的にテキストに合わせる
proc auto_scroll_linenumber { } {
      .text_linenumber yview [expr floor([.text index @3,3 ])]
}

proc auto_show_column_and_line { } {
    global cursor_now_x
    global cursor_now_y
    set cursor_now_x [ expr 1 + \
         [string length [.text get "insert linestart" insert]]]
    set cursor_now_y [ expr int(floor([.text index insert])) ]
}
