# $Revision$ on $Date$
# The above revision line needs to be the first line for all techtk files
# Copyright (c) 2006 Ansys Inc

#----------------------------------------------------------------------------
#
# PADT---------------------------------------------------- Phoenix Analysis &
#                                                         Design Technologies
#
#----------------------------------------------------------------------------
#
#       Package: AI-Environment GUI Management
#
#
#          File: gui_manager.tcl
#
#
#     Developer: Matt Sutton
#
#
#   Description: This file contains various support procedures for managing
#                the GUI.  This is the primary file for the entire GUI 
#                management system in that it sources all other files.
#   
#
#      03/25/02: Demo release 1. Limited/partial widget support. 
#                Limited error checking.
#
#           
#
#-----------------------------------------------------------------------------
# For support: 1-480-813-4884 ext. 125 -or- matt.sutton@padtinc.com
#-----------------------------------------------------------------------------

package require struct
package require BWidget 1.4.0

namespace eval ::AIEnv::GUIManagement {
    variable Context
    variable GUIDisplayData
    variable BuildContext
    variable CATRootDir
    variable TokenList
    variable CurTokenIndex
    variable GUIZones
    variable ItemNameMap
    variable DEZBuildData
    variable ActiveDEZ
    variable GreyFlagOnValue
    variable CacheDir
   
    global AIEnvDir AppName AIEnvProduct Version
    global env

    
    #-----------------------------------------------------------------------------
    #
    # Global variables / structures
    #

    # WindowPathStack is used during the construction of the gui items from the tree based
    # description.  The top of the stack contains the current TCL window path name.
    if {[info commands WindowPathStack] == ""} {
    	struct::stack WindowPathStack
	}

    # ParentStack is used during the construction of the gui trees.  The top of the stack
    # contains the node name for the current parent object
    if {[info commands ParentStack] == "" } {
		struct::stack ParentStack
	}
    
    # DEZRowCountStack is used during construction of the DEZ's.  The top of the stack
    # is the current row count for the current level in the DEZ.  The main DEZ is at level
    # 0 and the only widget that can change the current level is the frame widget.  A 
    # frame widget will push a new Row count onto the DEZRowCountStack during construction
    if {[info commands DEZRowCountStack] == "" } {
    	struct::stack DEZRowCountStack
	}
    
    # The grey flag stack is used during operation of the GUI to enforce parent child
    # relationships for the greying out of items
    if {[info commands GreyFlagStack] == "" } {
    	struct::stack GreyFlagStack
	}
    
    # The following three stacks are used during the parsing of the resource files 
    # The top of each stack contains the resource file that is currently being parsed. Files
    # are pushed onto the stack with the include command and popped off of the stack when
    # they are read to the end.
    if {[info commands ResourceFileIDStack] == "" } {
    	struct::stack ResourceFileIDStack
	}
	
	if {[info commands ResourceFileNameStack] == "" } {
    	struct::stack ResourceFileNameStack
	}
	
	if {[info commands ResourceFileLineNumberStack] == "" } {
    	struct::stack ResourceFileLineNumberStack
	}
	
	if {[info commands WidgetTypeStack] == "" } {
		struct::stack WidgetTypeStack
	}
    
    # GUITree is the tree structure used to describe the entire GUI structure for the 
    # application. Main branches include:
    # 1. Menu
    # 2. Icon
    # 3. DEZ
    # 4...
    if {[info commands MainTree] == ""} {
    	struct::tree MainTree
	}

	if {[info commands TreeTypeStack] == ""} {
		struct::stack TreeStack
	}
	
	TreeStack push MainTree
	
    # Context describes the current context while parsing the resource file.  This information
    # is used to determine when certain commands are valid.  Possible values include
    #
    # 1. Menu - Parsing the menu structure.  Set/Reset by ParseWinMenu
    # 2. Icon - Parsing the Icon structure.  Set/Reset by ParseIconBar/ParseTabbedIconBar
    # 3. DEZ  - Parsing the DEZ structure.   
    # 4. NULL - Base state. Not currently in anyone of the above structures
    set Context "NULL"
    
    namespace export DestroyGUIBuilder
    namespace export InitializeGUIBuilder
    
    # Figure out where the root directory of CAT is
    set CATRootDir $AIEnvDir
    
    # Source relavent scripts.
    
    set ActiveDEZ "NULL"
    	
    # Zero out the tokens list
    set TokenList ""
    set CurTokenIndex 0

    set GreyFlagOnValue 1
    
    set home_dir [lindex [glob -nocomplain ~] 0]
    set app aienv
    if [info exists AIEnvProduct] {
        append app _$AIEnvProduct
    }
    if [info exists env(AI_ENV_IN_WB)] {
        append app _wb
    }

    set CacheDir [file join $home_dir .${app}${Version}_cache]
    if { [file isdirectory $CacheDir] == 0 } {
        file mkdir $CacheDir
    }
}

proc ::AIEnv::GUIManagement::SetTreeFrame {win} {
    variable GUIZones
    set GUIZones("TreeFrame") $win
}

proc ::AIEnv::GUIManagement::SetAIEnvZones {} {
	variable GUIZones
	set GUIZones("MenuZone") .menuZone
	set GUIZones("IconZone") .iconZone.common
	set GUIZones("TabbedIconZone") .iconZone.tabbed
	set GUIZones("DataEntryZone") .treeDEZ.dez
	set GUIZones("PopupZone") .popupMen
}

#------------------------------------------------------------------------------
#
proc ::AIEnv::GUIManagement::SetCATRootDir {Dir} {
    variable CATRootDir 
    set CATRootDir $Dir
}
    
proc ::AIEnv::GUIManagement::InitializeGUIBuilder {} {
    variable ItemNameMap
    MainTree set "root" -key "Type" "Root"
    # Add the root branches to the tree
    set NodeID [MainTree insert "root" end]
    MainTree set $NodeID -key "Type" "MenuZone"
    set ItemNameMap("Menu") $NodeID

    set NodeID [MainTree insert "root" end]
    MainTree set $NodeID -key "Type" "IconZone"
    set ItemNameMap("Icon") $NodeID

    global aie_guiOpt
    variable TabbedIconZoneStyle
    set TabbedIconZoneStyle $aie_guiOpt(iconZoneStyle)

    if {$TabbedIconZoneStyle == "default"} {
       set NodeID [MainTree insert "root" end]
       MainTree set $NodeID -key "Type" "TabbedIconZone"
       set ItemNameMap("TabbedIcon") $NodeID
    }

    set NodeID [MainTree insert "root" end]
    MainTree set $NodeID -key "Type" "DataEntryZone"
    set ItemNameMap("DEZ") $NodeID

    set NodeID [MainTree insert "root" end]
    MainTree set $NodeID -key "Type" "DialogWindowRoot"
    set ItemNameMap("DialogWindowRoot") $NodeID
    
    set NodeID [MainTree insert "root" end]
    MainTree set $NodeID -key "Type" "HotKeyZone"
    set ItemNameMap("HotKey") $NodeID
	
    set NodeID [MainTree insert "root" end]
    MainTree set $NodeID -key "Type" "PopupZone"
    set ItemNameMap("Popup") $NodeID
    
    set NodeID [MainTree insert "root" end]
    MainTree set $NodeID -key "Type" "WizardColumnRoot"
    set ItemNameMap("WizardColumnRoot") $NodeID
	
	# The wizard root node must inserted be after the wizard column root node
	# so that upon the traversal of the tree, the wizard column root node is 
	# fully traversed before the wizard root node is traversed.
	
	set NodeID [MainTree insert "root" end]
	MainTree set $NodeID -key "Type" "WizardRoot"
    set ItemNameMap("WizardRoot") $NodeID
}

proc ::AIEnv::GUIManagement::DestroyGUIBuilder {} {
    # Clean up memory
    ResourceFileIDStack destroy
	ResourceFileNameStack destroy
    ResourceFileLineNumberStack destroy
    GreyFlagStack destroy
    DEZRowCountStack destroy
    WindowPathStack destroy
    ParentStack destroy
    Main_Tree destroy
    WidgetTypeStack destroy
    TreeStack destroy
    
    ::AIEnv::GUIManagement::DEZ::DestroyAll
}

proc ::AIEnv::GUIManagement::Error {ErrorReason} {
    error $ErrorReason
}

proc ::AIEnv::GUIManagement::ShowMessage {Type Message} {
    global AppName
    
    set Type [string toupper $Type]
    set Icon info
    switch $Type {
        "NOTE"  { set Icon info }
        "WARNING"  { set Icon warning }
        "ERROR"  { set Icon error }
        default  { set Icon info }
    }
    tk_messageBox -title $AppName -icon $Icon -message $Message -parent .
}

proc ::AIEnv::GUIManagement::SetIconZoneWidths {CommonIconZoneWidth TabbedIconZoneWidth} {
    variable GUIDisplayData
    set GUIDisplayData("IconZoneWidth") $CommonIconZoneWidth
    set GUIDisplayData("TabbedIconZoneWidth") $TabbedIconZoneWidth
}

proc ::AIEnv::GUIManagement::DEZHelp {NodeID} {
	ShowMessage NOTE "Sorry. Help for this item is not yet implemented."
}

# ----------------------------------------------------------------
#  to raise a TabbedIcon page by text name  --- Jigen
#  default: to get label name (i.e. "-text") of raised page --- VG

proc ::AIEnv::GUIManagement::TabbedIconPageRaise {{tName ""}} {
    variable TabbedIconBar

    if {![info exist TabbedIconBar] || ![winfo exist $TabbedIconBar] || ([winfo class $TabbedIconBar] != "NoteBook")} {
      return ""
    }
    if {$tName == ""} {
	    set pg [$TabbedIconBar raise]
        if {$pg != ""} {
	        return [$TabbedIconBar itemcget $pg -text]
	    }
    }
    foreach pg [$TabbedIconBar pages] {
        if {[$TabbedIconBar itemcget $pg -text] == $tName} {
            $TabbedIconBar raise $pg
            return $pg
        }
   }
   return ""
}

# ----------------------------------------------------------------
#  to test if a named page exist in TabbedIconBar --- Jigen

proc ::AIEnv::GUIManagement::TabbedIconPageExist {tName} {
   variable TabbedIconBar

   if {![info exist TabbedIconBar] || ![winfo exist $TabbedIconBar] \
          || ([winfo class $TabbedIconBar] != "NoteBook")} {
      return 0
   }

   foreach pg [$TabbedIconBar pages] {
      if {[$TabbedIconBar itemcget $pg -text] == $tName} {
         return 1
      }
   }
   return 0
}

#---------------------------------------------------------------------------------------------
#
# These functions create the advanced widgets used for editing AI-Environment lists or tables
#

proc ::AIEnv::GUIManagement::MultilineEntityChooser {ListVariableName} {
    
    # Create the multiline editor
    set Editor [::AIEnv::GUIManagement::Widgets::TextEditor::TextEditor]
    $Editor create "editable" "List Editor"
    $Editor loadlist $ListVariableName
    
}

proc ::AIEnv::GUIManagement::TableEditor {NodeID} {
	global AppName
	# Get the data associated with this table
	set NumCols [GUITree get $NodeID -key "NumCols"]
	set ColHeadings [GUITree get $NodeID -key "ColHeadings"]
	set TableArray [GUITree get $NodeID -key "TableVariable"]
	
	if { [array exists $TableArray] } {
		set TableEditor [::AIEnv::GUIManagement::Widgets::TableEditor::TableEditor]
		$TableEditor create $NumCols $ColHeadings $TableArray
	} else {
		tk_messageBox -message "The specifed array: \"$TableArray\" does not exist" -title $AppName
	}
}

proc ::AIEnv::GUIManagement::ValidateUserInput {DEZName VariableName Widget Type {LowerBound ""} {UpperBound ""}} {
	variable CurrentlyErrorChecking
	global AppName
	# We can only have one error ValidateUserInput running at a time or we enter
	# into a race condition
	if { [info exists CurrentErrorChecking] == 1 } {
		if { $CurrentlyErrorChecking == 1 } {
			return
		}
	}
	# We're error checking, so lock others out
	set CurrentlyErrorChecking 1
	
	set Val [$DEZName get $VariableName]
	
	set Type [string toupper $Type]
	set ValidInput 1
	set Reason ""
	
	# Keep things sane, ie make sure the lower bound is lower than the upper bound if they
	# both exist
	if { [expr {$LowerBound != ""} && {$UpperBound != ""}] } {
		if { $LowerBound > $UpperBound } {
			# Swap the bounds
			set Tmp $LowerBound
			set LowerBound $UpperBound
			set UpperBound $Tmp
		}
	}
	
	switch $Type {
		NUM {
			#Check to see if the user typed anything in first
			if { $Val != "" } {
				# Let tcl tell us if it is a number or not
				if { [catch {[expr abs($Val)]}] == 0 } {
					# Its a number, so check bounds if neccessary
					if { $LowerBound != "" } {
						if { $Val < $LowerBound } {
							set ValidInput 0
							set Reason "Please enter a number greater than $LowerBound"
						}
					}
					if { $UpperBound != "" } {
						if { $Val > $UpperBound } {
							set ValidInput 0
							set Reason "Please enter a number less than $UpperBound"
						}
					}
				} else {
					set ValidInput 0
					set Reason "The value: \"$Val\" could not be interpreted as a number. Please enter a numerical value"
				}
			} else {
				set ValidInput 0
				set Reason "Please enter a number"
			}
		}
		INTEGER {
			#Check to see if the user typed anything in first
			if { $Val != "" } {
				# Let tcl tell us if it is a number or not
				if { [catch [expr abs($Val)]] == 0 } {
					# Check if it is an integer
					set Tmp [expr int($Val)]
					if { $Tmp == $Val } {
						# Its a number and an integer, so check bounds if neccessary
						if { $LowerBound != "" } {
							if { $Val < $LowerBound } {
								set ValidInput 0
								set Reason "Please enter a number greater than $LowerBound"
							}
						}
						if { $UpperBound != "" } {
							if { $Val > $UpperBound } {
								set ValidInput 0
								set Reason "Please enter a number less than $UpperBound"
							}
						}
					} else {
						set ValidInput 0
						set Reason "The value: \"$Val\" could not be interpreted as an integer"
					}
				} else {
					set ValidInput 0
					set Reason "The value: \"$Val\" could not be interpreted as a number. Please enter a numerical value"
				}
			} else {
				set ValidInput 0
				set Reason "Please enter an integer number"
			}
		}
		TEXT {
			
		}
		default {
			
		}
	}
	
	if {$ValidInput == 0} {
		tk_messageBox -title "$AppName Invalid Input" -message $Reason -icon info -type ok
		set $VariableName ""
		focus $Widget
	} else {
		# All went well so set the error checking flag back to zero
		set CurrentlyErrorChecking 0
	}
}


#----------------------------------------------------------------------------------
#
# Procedure modifies OutFileName to contain the name of a valid file if it exists
# and returns 1 otherwise, it returns an empty string for OutFileName an the 
# reason why the call failed in ErrorReason
# Changed glob to glob_fixed because of a bug in mouting files from sgi to linux
# over NFS - Ganesan
#----------------------------------------------------------------------------------
proc ::AIEnv::GUIManagement::GetFileNameImpl {InFileName OutFileName ErrorReason} {
	upvar $OutFileName ModFileName
	upvar $ErrorReason Reason
	
	set Path [file dirname $InFileName]
	set Name [file tail $InFileName]
	
	set CurDir [pwd]
	
	if { [file isdirectory $Path] == 1 } {
		cd $Path
		set FileList [glob_fixed *]
		set FileList [join $FileList]
		set Found [regexp -nocase $Name $FileList TmpName]
		if { $Found == 1 } {
			# Rename the file so that future builds will succeed
		        catch {file rename -force $TmpName $Name}
			set ModFileName [file join $Path $Name]
			set Reason ""
			set ReturnCode 1
		} else {
			set ModFileName ""
			set Reason "Could not find a file named: \"$Name\" in directory: \"$Path\""
			set ReturnCode 0
		}
	
	} else {
		set ModFileName ""
		set Reason "The path: \"$Path\" is not a valid directory."
		set ReturnCode 0
	}
	
	cd $CurDir
	return $ReturnCode
}

# no need to check anything - Ganesan
proc ::AIEnv::GUIManagement::GetFileName {FileName} {
    variable FastBuild

    if [file exists $FileName] {
	return $FileName
    } 

    if { $FastBuild } {
	return $FileName
    } else {
	global AppName
	set OutName ""
	set Reason ""
	set Result [GetFileNameImpl $FileName OutName Reason]
	
	if { $Result == 1 } {
	    return $OutName
	} else {
	    error $Reason
	}
   }
}

proc ::AIEnv::GUIManagement::RuntimeError {Reason} {
    global errorInfo
    mess "$Reason\n" red
    if [info exists errorInfo] {
	mess "$errorInfo\n"
    }
    busy reset
}

proc ::AIEnv::GUIManagement::WidgetProc {WidgetName args} {
    if { [catch {eval "$WidgetName $args"} result] != 0 } {
	mess "$result\n" red
	busy reset
    }
}

# ------------------------------------------------------------------------

# This is used for binding a sequence of actions to a key, or other purposes

proc ::AIEnv::GUIManagement::InvokeUserActions {actions} {
    global ::AIEnv::GUIManagement::ExclusiveGroup::ExclusiveGroupData
    global ::AIEnv::GUIManagement::ActiveDEZ
    global auto_pick_mode 
    variable TabbedIconWidget
    
    set dez ""
    foreach act $actions {
	    switch [lindex $act 0] {
	        tab {
		        if {([lindex $act 1] == "Edit Mesh") && (![ic_uns_is_loaded] && ![ic_str_is_loaded])} {
		            if ![string match "*dez createElemsDEZ*" $actions] {
		                return 1
		            }
		        }
		        foreach p [$TabbedIconWidget pages] {
		            if {[$TabbedIconWidget itemcget $p -text] == [lindex $act 1]} {
			            $TabbedIconWidget raise $p
		            }
		        }
	        }
	        dez {
		        set dez [lindex $act 1]
                set reset_pick_mode 0
                if $auto_pick_mode {
                    set auto_pick_mode 0
                    set reset_pick_mode 1
                }
		        $dez show
                if $reset_pick_mode { set auto_pick_mode 1 }
	        }
	        exclusiveGroupIcon {
		        if {$dez == ""} {
		            set dez $::AIEnv::GUIManagement::ActiveDEZ
		        }
		        set index ""
		        set subwins $::AIEnv::GUIManagement::ExclusiveGroup::ExclusiveGroupData([lindex $act 1],Children,Name)
		        set c 0
		        foreach sw $subwins {
		            if {$sw == [lindex $act 2]} {
			            set index $c
			            break
		            }
		            incr c
		        }
		        if {$index == ""} { error "option not found in $act" }
		        set bbname ::AIEnv::GUIManagement::ExclusiveGroup::ExclusiveGroupData([lindex $act 1],ButtonBoxes)
		        set but ""
		        if [info exists $bbname] {
		            set index0 $index
		            foreach bb [set $bbname] {
			            set bch [winfo children $bb]
			            if {$index0 < [llength $bch]} {
			                set but [lindex $bch $index0]
			                break
			            }
			            incr index0 -[llength $bch]
		            }
		        }
		        if {$but != ""} {
		            $but invoke
		        } else {
		            set var $::AIEnv::GUIManagement::ExclusiveGroup::ExclusiveGroupData([lindex $act 1],variable)
		            ::AIEnv::GUIManagement::ExclusiveGroup::IconHandler [lindex $act 1] $index $var
		        }
	        }
	        greyGroup {
		        if {$dez == ""} { error "must specify a dez before $act" }
		        set var [lindex $act 1]Val
		        eval $dez set $var \{[lindex $act 2]\}
	        }
	        dropDownEntry - checkItem - textEntry {
		        if {$dez == ""} { error "must specify a dez before $act" }
		            eval $dez set [lindex $act 1] \{[lindex $act 2]\}
	        }
	        entityChooser {
		        set ok [::AIEnv::GUIManagement::Picker::_pick [lindex $act 1]]
		        if !$ok {
		            puts cancel
		            return 0
		        }
	        }
	        apply {
		        if {$dez == ""} { error "must specify a dez before $act" }
		            eval $dez apply
	        }
	        default {
		        error "[lindex $act 0] not a valid option\n"
	        }
	    }
	    update
    }
    return 1
}
