##############################################################################
#    TCL Developer Studio
#
#    Copyright (C) 1999  Alexey Kakunin
#    small@star.spb.ru
#
#    Parts are recoded by Andreas Sievers
#    andreas.sievers@t-online.de
#
# 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
# of the License, 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.
#
##############################################################################

namespace eval editorWindows {

namespace export setBindings create selectAll
namespace export gotoMark gotoProc findNext setCursor replace replaceAll
namespace export enableHL disableHL onTabSize onFontChange

variable This
variable TxtWidget ""
variable Text ""
variable UndoID

proc setBindings {} {
    global tcl_platform
    global EditorData
    variable TxtWidget
    

    set tabSize [expr {$EditorData(options,tabSize)* [font measure $EditorData(options,fonts,editorFont) -displayof $TxtWidget " "]}]
   
    for {set i 1} {$i < 11} {incr i} {
        set tab [expr {$tabSize * $i}]
        set tabList [lappend tabList $tab]
    }
     
    
    $TxtWidget configure -wrap none -font $EditorData(options,fonts,editorFont) -tabs $tabList

    ConfigureTags
    
    # create bindings
    bind $TxtWidget <Tab> "editorWindows::OnTabPress; break"
    
    if {$tcl_platform(platform) != "windows"} {
        bind $TxtWidget <Control-Insert> "editorWindows::copy; break"
        bind $TxtWidget <Control-Delete> "editorWindows::cut; break"
        bind $TxtWidget <Shift-Insert>   "editorWindows::paste; break"
        bind $TxtWidget <Control-a> "editorWindows::selectAll; break"
    }
    
    bind $TxtWidget <KeyRelease-Return> "editorWindows::IndentCurLine;editorWindows::OnKeyRelease"
    bind $TxtWidget <KeyRelease-braceright> "editorWindows::IndentCurLine;editorWindows::OnKeyRelease"
    bind $TxtWidget <KeyRelease-space> "editorWindows::OnSpaceRelease;editorWindows::OnKeyRelease"
    bind $TxtWidget <KeyRelease-braceleft> "editorWindows::OnLeftBraceRelease;editorWindows::OnKeyRelease"
    bind $TxtWidget <KeyRelease-parenleft> "editorWindows::OnLeftParenRelease;editorWindows::OnKeyRelease"
    bind $TxtWidget <KeyRelease-bracketleft> "editorWindows::OnLeftBracketRelease;editorWindows::OnKeyRelease"
    bind $TxtWidget <KeyRelease-quotedbl> "editorWindows::OnQuoteDblRelease;editorWindows::OnKeyRelease"
    bind $TxtWidget <KeyRelease-Delete> "editorWindows::OnKeyDelete"
    bind $TxtWidget <Control-h> "editorWindows::OnKeyDelete"
	bind $TxtWidget <KeyRelease> editorWindows::OnKeyRelease
    # Workaround for german keyboard by Rasmus Debisch
    # Dt (6/1/1999)
    bind $TxtWidget <KeyPress> {editorWindows::OnDispatchExtra %A}
	# end of Workaround
    bind $TxtWidget <ButtonRelease> editorWindows::OnMouseRelease
    bind $TxtWidget <Control-x> "editorWindows::cut; break"
    bind $TxtWidget <Control-c> "editorWindows::copy; break"
    bind $TxtWidget <Control-v> "editorWindows::paste; break"
    bind $TxtWidget <KeyRelease-Home> "editorWindows::gotoFirstChar;break"
        
    return
}

# Workaround for german keyboard by Rasmus Debisch
# Dt (6/1/1999)
proc OnDispatchExtra {key} {
    switch -- $key {
        \{ {editorWindows::OnLeftBraceRelease}
        \[ {editorWindows::OnLeftBracketRelease}
    }
#    editorWindows::OnKeyRelease
}


    
        
proc gotoFirstChar {} {
    variable TxtWidget
    
    set curPos [$Editor::current(text) index insert]
    set result [Editor::getFirstChar $curPos]
    $TxtWidget mark set insert [lindex $result 1]
    
}    
    
    
    
# edit-copy
proc copy {} {
    variable TxtWidget

    if {[catch {$TxtWidget index sel.first}]} {
        return
    }
    
    set lineStart [lindex [split [$TxtWidget index sel.first] "."] 0]
    set lineEnd [lindex [split [$TxtWidget index sel.last] "."] 0]
    
    tk_textCopy $TxtWidget
    
    ReadCursor
    ColorizeLines $lineStart $lineEnd
    
    return
}

# edit-cut
proc cut {} {
    variable TxtWidget

    if {$TxtWidget == ""} {
        return
    }

    tk_textCut $TxtWidget

    ReadCursor
    
    set lineNum [lindex [split [$TxtWidget index insert] "."] 0]

    ColorizeLines $lineNum $lineNum
    
    return
}

# edit-paste
proc paste {} {
    global tcl_platform
    variable TxtWidget

    if {$TxtWidget == ""} {
        return
    }

    if {"$tcl_platform(platform)" == "unix"} {
        catch { $TxtWidget delete sel.first sel.last }
    }
    
    if {[$TxtWidget tag ranges sel] == ""} {
        set lineStart [lindex [split [$TxtWidget index insert] "."] 0]
    } else  {
        set lineStart [lindex [split [$TxtWidget index sel.first] "."] 0]
    }    
    tk_textPaste $TxtWidget
    $TxtWidget see insert
    update idletasks

    ReadCursor
    
    set lineEnd [lindex [split [$TxtWidget index insert] "."] 0]
    ColorizeLines $lineStart $lineEnd
    
    return
}

# edit-delete
proc delete {} {
    global tcl_platform
    variable TxtWidget

    if {$TxtWidget == ""} {
        return
    }

    catch { $TxtWidget delete sel.first sel.last }

    ReadCursor
    set lineNum [lindex [split [$TxtWidget index insert] "."] 0]

    ColorizeLines $lineNum $lineNum
    
    return
}

proc selectAll {} {
    variable TxtWidget

    if {$TxtWidget == ""} {
        return
    }
        
    $TxtWidget tag add sel 0.0 end

}

# set cursor to the function
proc gotoMark { markName } {
    global EditorData    
    variable TxtWidget

    $TxtWidget mark set insert $markName
    $TxtWidget see insert
    focus $TxtWidget
    ReadCursor 0
    flashLine
}

proc gotoProc {procName} {
    global EditorData
    variable TxtWidget
    
    set expression "^( |\t|\;)*proc( |\t)+($procName)+( |\t)"
    set result [$TxtWidget search -regexp -- $expression insert]
    
    if {$result != ""} {
        $TxtWidget mark set insert $result
        $TxtWidget see insert
        focus $TxtWidget
        ReadCursor 0
        flashLine
    }    
    return
}

proc flashLine {} {
    variable TxtWidget
    $TxtWidget tag add procSearch "insert linestart" "insert lineend"
    $TxtWidget tag configure procSearch -background yellow
    after 2000 {catch {$editorWindows::TxtWidget tag delete procSearch} }
    return
}    
    
    
# parse file and create proc file
proc ReadMarks { fileName } {
    global EditorData
    variable TxtWidget

    # clear all marks in this file
    foreach name [array names EditorData files,$EditorData(curFile),marks,] {
        unset EditorData($name)
    }

    set EditorData(files,$fileName,marks) {}
    
    set result [$TxtWidget search -forwards "proc " 1.0 end]
    
    while {$result != ""} {
        set lineNum [lindex [split $result "."] 0]
        set line [$TxtWidget get $lineNum.0 "$lineNum.0 lineend"]
        set temp [string trim $line \ \t\;]
        if {[scan $temp %\[proc\]%s proc name] == 2} {
            if {$proc == "proc"} {                
                set markName $name 
                lappend EditorData(files,$fileName,marks) $markName
#                set EditorData(files,$fileName,marks,$markName,lineNum) $lineNum
                set EditorData(files,$fileName,marks,$markName,name) $name
            }
        }
        set result [$TxtWidget search -forwards "proc" "$result lineend" end ]      
    }
    return
}


proc IndentCurLine {} {
    variable TxtWidget
    
    IndentLine [lindex [split [$TxtWidget index insert] "."] 0]    
}

proc IndentLine {lineNum} {
    variable TxtWidget
    global EditorData

    if {$EditorData(options,useSintaxIndent)} {
        if {$lineNum > 1} {
        # get previous line text
            set prevText [$TxtWidget get "$lineNum.0-1l" "$lineNum.0-1l lineend"]
            set prevSpaces ""
            regexp "^(\ |\t)*" $prevText prevSpaces
            set braces [CountBraces $prevText]
            if {$braces > 0} {
                #indent
                while {$braces != 0} {
                    append prevSpaces $EditorData(indentString)
                    incr braces -1
                }
                $TxtWidget insert $lineNum.0 $prevSpaces
                return
            } elseif {$braces == 0} {
                set prevText [$TxtWidget get "$lineNum.0-1l" "$lineNum.0-1l lineend"]
                set prevSpaces ""
                regexp "^(\ |\t)*" $prevText prevSpaces
                $TxtWidget insert $lineNum.0 $prevSpaces
                return
            } else {
                #unindent
                set result [GetOpenPair "\}" "$lineNum.0-1l"]
                if {$result == ""} {
                        return
                } else  {
                    incr braces
                }
                while {$braces != 0} {
                    set result [GetOpenPair "\}" "$result-1c"]
                    if {$result == ""} {
                        break
                    } else  {
                        incr braces
                    }
                }
                set prevText [$TxtWidget get "$result linestart" "$result lineend"] 
                set prevSpaces ""
                regexp "^(\ |\t)*" $prevText prevSpaces
                $TxtWidget insert $lineNum.0 $prevSpaces
                return
            }
        }
    } elseif {$EditorData(options,useIndent)} {
        if {$lineNum > 1} {
        # get previous line text
            set prevText [$TxtWidget get "$lineNum.0-1l" "$lineNum.0-1l lineend"]
            regexp "^(\ |\t)*" $prevText spaces
            set braces [CountBraces $prevText]
            if {$braces > 0} {
                #indent
                $TxtWidget insert $lineNum.0 $spaces
                return
            }
        }
    } else  {
        return
    }
}
    
proc indentSelection {} {
    variable TxtWidget
    global tclDevData
    
    #Gets start and end lines
    if {[catch {set selFirst [$TxtWidget index sel.first]}] ||
        [catch {set selLast [$TxtWidget index sel.last]}] ||
        [catch {set anchor [$TxtWidget index anchor]}]} {
        #nothing to do
        return
    }
    
    set startLine [lindex [split [$TxtWidget index sel.first] "."] 0]
    set endLine [lindex [split [$TxtWidget index sel.last] "."] 0]
    if {$endLine == [lindex [split [$TxtWidget index end] "."] 0]} {
        #skip last line in widget
        incr endLine -1
    }
    
    for {set lineNum $startLine} {$lineNum <= $endLine} {incr lineNum} {
        set text " "
        append text [$TxtWidget get "$lineNum.0" "$lineNum.0 lineend"]
        
        $TxtWidget delete "$lineNum.0" "$lineNum.0 lineend"
        $TxtWidget insert "$lineNum.0" $text
    }
    # highlight
    ColorizeLines $startLine $endLine

    # set selection
    $TxtWidget tag add sel $selFirst $selLast
    $TxtWidget mark set anchor $anchor
    
    return
}

proc unindentSelection {} {
    variable TxtWidget
    global tclDevData
    
    #Gets start and end lines
    if {[catch {set selFirst [$TxtWidget index sel.first]}] ||
        [catch {set selLast [$TxtWidget index sel.last]}] ||
        [catch {set anchor [$TxtWidget index anchor]}]} {
        #nothing to do
        return
    }
    
    set startLine [lindex [split [$TxtWidget index sel.first] "."] 0]
    set endLine [lindex [split [$TxtWidget index sel.last] "."] 0]
    if {$endLine == [lindex [split [$TxtWidget index end] "."] 0]} {
        #skip last line in widget
        incr endLine -1
    }
    
    for {set lineNum $startLine} {$lineNum <= $endLine} {incr lineNum} {
        if {[$TxtWidget get "$lineNum.0" "$lineNum.0 +1 char"] == " "} {
            $TxtWidget delete "$lineNum.0" "$lineNum.0 +1 char"
        }
    }
    # highlight
    ColorizeLines $startLine $endLine

    # set selection
    $TxtWidget tag add sel $selFirst $selLast
    $TxtWidget mark set anchor $anchor
    
    return
}
    
# change tab to spaces
proc OnTabPress {} {
    variable TxtWidget
    global EditorData
    
    if {$EditorData(options,changeTabs)} {
        set spaces ""
        
        for {set i 0} {$i < $EditorData(options,tabSize)} {incr i} {
            append spaces " "
        }
        
        #insert spaces
        $TxtWidget insert insert $spaces
    } else {
        #insert tab
        $TxtWidget insert insert "\t"
    }
    Editor::checkProcs
}

proc OnKeyDelete {} {
    variable TxtWidget
    
    ColorizeLine [lindex [split [$TxtWidget index insert] .] 0]
    Editor::updateProcs
    Editor::checkProcs
}

# reaction on key releasing
proc OnKeyRelease {} {
    variable TxtWidget
    
    set lineNum [lindex [split [$TxtWidget index insert] "."] 0]
    Editor::checkProcs
    set Editor::current(lastPos) [$Editor::current(text) index insert]
    ReadCursor
    ColorizeLine $lineNum
    ColorizePair
    set Editor::current(procListHistoryPos) 0
}

#reaction on space release
proc OnSpaceRelease {} {
    global EditorData
    variable TxtWidget
    
    if {!$EditorData(options,useTemplates) || !$EditorData(options,useTemplatesForKeywords)} {
        return
    }
    
    set templateKeyword [GetTemplateKeyword [$TxtWidget get "insert linestart" "insert lineend"]]
    set curPos [$TxtWidget index insert]
    set lineNum [lindex [split $curPos "."] 0]
    
    switch -- $templateKeyword {
        "if" {
            $TxtWidget insert insert " \{\n\}"
            incr lineNum
            IndentLine $lineNum
            $TxtWidget mark set insert $curPos
        }
        
        "for" {
            $TxtWidget insert insert "\{\} \{\} \{\} \{\n\}"
            incr lineNum
            IndentLine $lineNum
            $TxtWidget mark set insert "$curPos +1ch"
        }
        
        "foreach" {
            $TxtWidget insert insert " \{\n\}"
            incr lineNum
            IndentLine $lineNum
            $TxtWidget mark set insert $curPos
        }
        
        "while" {
            $TxtWidget insert insert " \{\n\}"
            incr lineNum
            IndentLine $lineNum
            $TxtWidget mark set insert $curPos
        }
        
        "switch" {
            $TxtWidget insert insert " \{\n\}"
            incr lineNum
            IndentLine $lineNum
            $TxtWidget mark set insert $curPos
        } 
        
        "proc" {
            $TxtWidget insert insert " \{\} \{\n\}"
            incr lineNum
            IndentLine $lineNum
            $TxtWidget mark set insert $curPos
        }
        
        "else" {
            $TxtWidget insert insert " \{\n\n\}"
            ColorizeLine $lineNum
            incr lineNum
            IndentLine $lineNum
            $TxtWidget mark set insert "$lineNum.0 lineend"
            incr lineNum
            IndentLine $lineNum
        }
        
        "elseif" {
            $TxtWidget insert insert " \{\n\}"
            ColorizeLine $lineNum
            incr lineNum
            IndentLine $lineNum
            $TxtWidget mark set insert $curPos
        }
    }
    Editor::checkProcs
    return 0
}

proc OnLeftBraceRelease {} {
    variable TxtWidget
    global EditorData

    if {!$EditorData(options,useTemplates) || !$EditorData(options,useTemplatesForBrace)} {
        return
    }

    set curPos [$TxtWidget index insert]
    $TxtWidget insert insert "\}"
    $TxtWidget mark set insert $curPos
    Editor::checkProcs
    return
}

proc OnLeftParenRelease {} {
    variable TxtWidget
    global EditorData

    if {!$EditorData(options,useTemplates) || !$EditorData(options,useTemplatesForParen)} {
        return
    }

    set curPos [$TxtWidget index insert]
    $TxtWidget insert insert "\)"
    $TxtWidget mark set insert $curPos
    Editor::checkProcs
    return
}

proc OnLeftBracketRelease {} {
    variable TxtWidget
    global EditorData

    if {!$EditorData(options,useTemplates) || !$EditorData(options,useTemplatesForBracket)} {
        return
    }

    set curPos [$TxtWidget index insert]
    $TxtWidget insert insert "\]"
    $TxtWidget mark set insert $curPos
    Editor::checkProcs
    return
}

proc OnQuoteDblRelease {} {
    variable TxtWidget
    global EditorData

    if {!$EditorData(options,useTemplates) || !$EditorData(options,useTemplatesForQuoteDbl)} {
        return
    }

    set curPos [$TxtWidget index insert]
    $TxtWidget insert insert "\""
    $TxtWidget mark set insert $curPos
    Editor::checkProcs
    return
}


# reaction on mouse button release
proc OnMouseRelease {} {
    ReadCursor
    ColorizePair
    Editor::checkProcs
    set Editor::current(lastPos) [$Editor::current(text) index insert]
    set Editor::current(procListHistoryPos) 0
}

# read information about cursor and set it to the global variables
proc ReadCursor {{selectProc 1}} {
    variable TxtWidget
    global EditorData
    
    set insertPos [split [$TxtWidget index insert] "."]
    set EditorData(cursor,line) [lindex $insertPos 0]
    set EditorData(cursor,pos) [expr {[lindex $insertPos 1] }]
    set EditorData(cursorPos) "Line: $EditorData(cursor,line)   Pos: $EditorData(cursor,pos)"
    set Editor::lineNo $EditorData(cursor,line)
    return
}


proc enableHL {} {
    variable TxtWidget
    
    if {$TxtWidget != ""} {
        colorize
    }
    
    return
}

proc disableHL {} {
    variable TxtWidget
    
    if {$TxtWidget != ""} {
        # delete all tags
        $TxtWidget tag delete comment
        $TxtWidget tag delete keyword

        ConfigureTags
    }
    
    return
}

proc colorize {} {
    variable TxtWidget
    variable EditorData

    # get number of lines
    set lineEnd [lindex [split [$TxtWidget index end] "."] 0]
    
    ColorizeLines 1 $lineEnd
}

proc ColorizeLines {StartLine EndLine} {
    variable TxtWidget
    
    # delete all tags
    $TxtWidget tag remove comment "$StartLine.0" "$EndLine.0 lineend"
    $TxtWidget tag remove keyword "$StartLine.0" "$EndLine.0 lineend"

    for {set lineNum $StartLine} {$lineNum <= $EndLine} {incr lineNum} {
        ColorizeLine $lineNum
    }
    
    return
}

proc ColorizeLine {lineNum} {
    variable TxtWidget
    global EditorData
    
    if {!$EditorData(options,useHL)} {
        return
    }
    
 #   get line
    set line [$TxtWidget get $lineNum.0 "$lineNum.0 lineend"]
    
    set range [IsComment $line $lineNum]
    if {$range != {}} {
        # this is comment
        #set comment font
        eval $TxtWidget tag remove keyword $range
        eval $TxtWidget tag add comment $range
    } else {
        $TxtWidget tag remove comment $lineNum.0 "$lineNum.0 lineend"
        set range [GetKeywordCoord $line $lineNum]            
        if {$range != {} } {
            eval $TxtWidget tag add keyword $range
        } else {
            $TxtWidget tag remove keyword $lineNum.0 "$lineNum.0 lineend"
        }
    }
    return    
}

proc ConfigureTags {} {
    variable TxtWidget
    global EditorData

    # blue is specially for Lapshin
    $TxtWidget tag configure comment -font $EditorData(options,fonts,commentFont) -foreground blue
    $TxtWidget tag configure keyword -font $EditorData(options,fonts,keywordFont)
    $TxtWidget tag configure pair -background red

    return    
}

proc IsComment {line lineNum} {
    variable TxtWidget
    
    set a ""
    regexp "^( |\t)*\#" $line a
     
    if {$a != ""} {
        return [list $lineNum.[expr [string length $a]-1] $lineNum.[string length $line]]
    } else {
        regexp "^(.*\;( |\t)*)\#" $line a
        if {$a != ""} {
            $TxtWidget tag remove comment $lineNum.0 "$lineNum.0 lineend"
            set range [GetKeywordCoord $line $lineNum]            
            if {$range != {} } {
                eval $TxtWidget tag add keyword $range
            } else {
                $TxtWidget tag remove keyword $lineNum.0 "$lineNum.0 lineend"
            }
            return [list $lineNum.[expr [string length $a]-1] $lineNum.[string length $line]]
        } else  {
            return {}
        }
    }
}

proc GetKeywordCoord {line lineNum} {
    global EditorData
    
    set name ""
    
    set temp [string trim $line \ \t\;\{\[\]\}] 
    if {![scan $temp %s name]} {
        return {}
    }
    
    set nameStart [string first $name $line]
    set nameEnd [string wordend $line $nameStart]

    # is it keyword?
    if {[lsearch $EditorData(keywords) $name] != -1 || $name == "else" || $name == "elseif"} {
        return [list $lineNum.$nameStart $lineNum.$nameEnd]
    } else  {
        return {}
    }
}


proc GetTemplateKeyword { line } {
    global EditorData
    
    set a ""
    regexp "^( |\t|\;)*\[a-z\]+ $" $line a
            
    if {$a != ""} {
        # gets name
        set b ""
        regexp "^( |\t)*" $line b
        set nameStart [string length $b]
        set nameEnd [string length $a]
        set name [string range $a [string length $b] end]

        #return name without last space
        return [string range $name 0 [expr {[string length $name] - 2}]]
    } else {
        # check for else
        set a ""
        regexp "^( |\t)*\}( |\t)*else $" $line a
        
        if {$a != ""} {
            return "else"
        }
        
        # check for elseif
        set a ""
        regexp "^( |\t)*\}( |\t)*elseif $" $line a
        
        if {$a != ""} {
            return "elseif"
        }
    }
 
    return ""
}

proc setCursor {lineNum pos} {
    variable TxtWidget
    
    $TxtWidget mark set insert $lineNum.$pos
    $TxtWidget see insert
    focus $TxtWidget
    ReadCursor
    
    return
}
    
#reaction on changing tab size
proc onTabSize {} {
    variable TxtWidget
    global EditorData
    
    if {$TxtWidget != ""} {
        set size [expr {$EditorData(options,tabSize)*
                    [font measure $EditorData(options,fonts,editorFont) -displayof $TxtWidget " "]}]
        $TxtWidget configure -tabs [list $size]
    }
    
    return
}

# reaction on change font
proc onFontChange {} {
    variable TxtWidget
    global EditorData
    
    if {$TxtWidget != ""} {
        $TxtWidget configure -font $EditorData(options,fonts,editorFont)
        ConfigureTags
    }
    
    return
}

################################################################################
# proc onChangeFontSize
#     added by A.Sievers    
################################################################################
proc onChangeFontSize {editWin} {
    global EditorData    
      
    if {$editWin != ""} {
        $editWin configure -font editorFont
        $editWin tag configure comment -font commentFont -foreground blue
        $editWin tag configure keyword -font keywordFont
        $editWin tag configure pair -background red
        update
    }
    return
}
    
    
proc GetOpenBraceLine {symbol {line ""}} {
    variable TxtWidget
    
    if {$line == ""} {
        set index insert
        set searchback 1
        set line [$TxtWidget get "$index linestart" $index]
        set lineNum [lindex [split [$TxtWidget index $index] "."] 0]
    } else {
        set searchback 0
    }

    set count 0
    set result [ProcessLineForOpenSymbol $line $symbol count]
    if {$result != ""} {
        #found
        return $result
    }
    if {!$searchback} {
        return ""
    }
    incr lineNum -1
    while {$lineNum > 0} {
        set line [$TxtWidget get "$lineNum.0" "$lineNum.0 lineend"]
        set result [ProcessLineForOpenSymbol $line $symbol count]
        
        if {$result != ""} {
            return $lineNum.$result
        }
        
        incr lineNum -1
    }
    
    return ""
}

proc GetOpenPair {symbol {index ""}} {
    variable TxtWidget
    
    if {$index == ""} {
        set index insert
    }

    set count 0
    set line [$TxtWidget get "$index linestart" $index]
    set lineNum [lindex [split [$TxtWidget index $index] "."] 0]
    
    set result [ProcessLineForOpenSymbol $line $symbol count]
    if {$result != ""} {
        #found
        return $lineNum.$result
    }
    
    incr lineNum -1
    while {$lineNum > 0} {
        set line [$TxtWidget get "$lineNum.0" "$lineNum.0 lineend"]
        set result [ProcessLineForOpenSymbol $line $symbol count]
        
        if {$result != ""} {
            return $lineNum.$result
        }
        
        incr lineNum -1
    }
    
    return ""
}


proc GetClosePair {symbol {index ""}} {
    variable TxtWidget
    
    if {$index == ""} {
        set index "insert +1ch"
    }

    set count 0
    set line [$TxtWidget get $index "$index lineend"]
    set lineNum [lindex [split [$TxtWidget index $index] "."] 0]
    set lineNums [lindex [split [$TxtWidget index end] "."] 0]
    
    set result [ProcessLineForCloseSymbol $line $symbol count]
    if {$result != ""} {
        #found
        incr result [lindex [split [$TxtWidget index $index] "."] 1]
        return $lineNum.$result
    }
    
    incr lineNum
    while {$lineNum < $lineNums} {
        set line [$TxtWidget get "$lineNum.0" "$lineNum.0 lineend"]
        set result [ProcessLineForCloseSymbol $line $symbol count]
        
        if {$result != ""} {
            return $lineNum.$result
        }
        
        incr lineNum
    }
    
    return ""
}

#process line for openSymbol
proc ProcessLineForOpenSymbol {line symbol countName} {
    upvar $countName count
    
    switch -- $symbol {
        "\}" {
            set openSymbol "\{"
        }
        "\]" {
            set openSymbol "\["
        }
        "\)" {
            set openSymbol "\("
        }
    }
    
    #process line
    for {set i [expr {[string length $line] - 1}]} {$i >= 0} {incr i -1} {
        set curChar [string index $line $i]
        
        if {$curChar == $openSymbol} {
                # increment count
                if {[string index $line [expr {$i - 1}]] == "\\"} {
                    #skip it
                    incr i -1
                } else  {
                    incr count
                    if {$count > 0} {
                        return $i
                    }
                }
        } elseif {$curChar == $symbol } {
                # decrement count
                if {[string index $line [expr {$i - 1}]] == "\\"} {
                    #skip it
                    incr i -1
                } else  {
                    incr count -1
                }
        }
    }
    
    return ""
}


#process line for closeSymbol
proc ProcessLineForCloseSymbol {line symbol countName} {
    upvar $countName count
    
    switch -- $symbol {
        "\{" {
            set closeSymbol "\}"
        }
        "\[" {
            set closeSymbol "\]"
        }
        "\(" {
            set closeSymbol "\)"
        }
    }
    
    #process line
    set len [string length $line]
    for {set i 0} {$i < $len} {incr i } {
        set curChar [string index $line $i]
        
        if {$curChar == $closeSymbol} {
            # increment count
            incr count
            if {$count > 0} {
                return $i
            }
        } elseif {$curChar == $symbol } {
            # decrement count
            incr count -1
        } elseif {$curChar == "\\"} {
            #skip next symbol
            incr i
        }
    }
    
    return ""
}

# count braces in line
proc CountBraces {line {count 0}} {
    set len [string length $line]
    
    for {set i 0} {$i < $len} {incr i} {
        switch -- [string index $line $i] {
            "\{" {
                # increment count
                incr count
            }
            
            "\}" {
                # decrement count
                incr count -1
            }
            
            "\\" {
                #skip next symbol
                incr i
            }
        }
    }
    
    return $count
}

# colorize pair
proc ColorizePair {} {
    variable TxtWidget

    $TxtWidget tag remove pair 0.0 end
    
    #get current char
    set curChar [$TxtWidget get insert]

    switch -- $curChar {    
        "\}" {
                set result [GetOpenPair "\}"]
                if {$result != ""} {
                    $TxtWidget tag add pair $result "$result +1ch"
                }
             }
        "\]" {
                set result [GetOpenPair "\]"]
                if {$result != ""} {
                    $TxtWidget tag add pair $result "$result +1ch"
                }
             }
        "\)" {
                set result [GetOpenPair "\)"]
                if {$result != ""} {
                    $TxtWidget tag add pair $result "$result +1ch"
                }
             }
        "\{" {
                set result [GetClosePair "\{"]
                if {$result != ""} {
                    $TxtWidget tag add pair $result "$result +1ch"
                }
             }
        "\[" {
                set result [GetClosePair "\["]
                if {$result != ""} {
                    $TxtWidget tag add pair $result "$result +1ch"
                }
             }
        "\(" {
                set result [GetClosePair "\("]
                if {$result != ""} {
                    $TxtWidget tag add pair $result "$result +1ch"
                }
             }
        default {return}
    }    
}
}
