# Copyright (c) 2000, Red Hat, Inc.
# 
# This file is part of Source-Navigator.
# 
# Source-Navigator 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.
# 
# Source-Navigator 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.
# 
# You should have received a copy of the GNU General Public License along
# with Source-Navigator; see the file COPYING.  If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
# MA 02111-1307, USA.
# 
########################################
##
## Make (Find in Files...)
##
########################################
itcl_class Make& {
	constructor {{config ""}} {
		global sn_options
		
		set class [$this info class]
		@@rename $this $this-tmp-
		@@frame $this -class $class -bd 0
		@@rename $this $this-win-
		@@rename $this-tmp- $this
		
		set topw [winfo toplevel $this]
		set s $this
		
		#add menu entries
		if {$menu != ""} {
		}
		#add toolbar icons
		if {$toolbar != ""} {
			set makefr $toolbar.makefr
			pack [frame $makefr] -side left
			#prev
			button $makefr.prev \
				-takefocus 0 \
				-text [get_indep String Prev] \
				-image del_image \
				-command "$this next_entry -1"
			balloon_bind_info $makefr.prev [get_indep String PrevINFO]
			pack $makefr.prev -side left -fill x -expand n
			#next
			button $makefr.next \
				-takefocus 0 \
				-text [get_indep String Next] \
				-image add_image \
				-command "$this next_entry +1"
			balloon_bind_info $makefr.next [get_indep String NextINFO]
			pack $makefr.next -side left -fill x -expand n
		}
		
		#Frame for command input
		set input $this.input
		frame $input
		pack $input -side top -padx 5 -fill x
		
		set entries $input.entries
		frame $entries
		pack $entries -fill x -expand y -side left -anchor nw -fill x

		#command string
		Combo& $entries.entry \
				-labelwidth 14 \
				-anchor ne \
				-label [get_indep String MakeEntry] \
				-textvariable $this-makecommand \
				-exportselection n \
				-expand y \
				-filter "" \
				-contents "" \
				-selectcommand "$this make_select_command $entries.entry" \
				-readonly 0 \
				-sort ""
		balloon_bind_info [$entries.entry entry] [get_indep String MakeEntryINFO]
		bind [$entries.entry entry] <Return> "watch y $this; $this ExecMake; watch n $this"
		pack $entries.entry -side top -expand y -fill x -anchor nw

		#add choose button for the make command to evtl. choose an executable
		button $entries.entry.choose \
				-text [get_indep String Choose] \
				-command [list	sn_choose_file \
								[$entries.entry entry] \
								$sn_options(executable_defaultext)]
		balloon_bind_info $entries.entry.choose [get_indep String ChooseINFO]
		pack $entries.entry.choose -side right -before $entries.entry.arrow
		
		#starting directory
		Combo& $entries.dir \
				-labelwidth 14 \
				-anchor ne \
				-label [get_indep String MakeStartDir] \
				-textvariable $this-startdir \
				-exportselection n \
				-expand y \
				-filter "" \
				-contents "" \
				-readonly 0 \
				-sort ""
		[$entries.dir entry] select to end
		bind [$entries.dir entry] <Return> "$this ExecMake"
		pack $entries.dir -side top -expand y -fill x -anchor nw

		button $entries.dir.choose \
				-text [get_indep String Choose] \
				-command [list sn_choose_dir [$entries.dir entry]]
		balloon_bind_info $entries.dir.choose [get_indep String ChooseINFO]
		pack $entries.dir.choose -side right -before $entries.dir.arrow

		# targets list
		Combo& $entries.targets \
				-labelwidth 14 \
				-anchor ne \
				-label "Build Targets" \
				-textvariable $this-target \
				-exportselection n \
				-expand y \
				-filter "" \
				-contents "" \
				-readonly 1 \
				-sort "" \
				-selectcommand "$this targets_cb"
		pack $entries.targets -side top -expand y -fill x -anchor nw

		# Setting the target list.
		set targets_list [list "<External Makefile>"]
		eval lappend targets_list [GetTargetsList]
		$entries.targets contents $targets_list

		# Selecting the last target used, if any.
		if {! [catch {paf_db_proj get -key IDEMakeCurrentTarget} targ]} {
			set index [lsearch -exact $targets_list $targ]
			if {$index == -1} {
				set index 0
			}
		} else {
			set index 0
		}
		$entries.targets select $index
		global $this-target
		@@set $this-target [lindex $targets_list $index]
		
		#display history for make entries
		make_add_history $entries.entry $entries.dir

		#Buttons start
		set btns $input.buttons
		frame $btns
		pack $btns -side right -anchor nw -fill x
		button $btns.start \
				-text [get_indep String StartMake]\
				-command "watch y $topw; $this ExecMake; watch n $topw"
		balloon_bind_info $btns.start [get_indep String StartMakeINFO]
		pack $btns.start -side top -padx 8 -pady 4 -fill x
		
		#Button cancel, if needed
		button $btns.cancel \
				-takefocus 0 \
				-text "Stop" \
				-command "$this close_make cancel" \
				-state disabled
		balloon_bind_info $btns.cancel [get_indep String CancelMakeINFO]
		pack $btns.cancel -side top -padx 8 -pady 4 -fill x

	        button $btns.launch \
		                -takefocus 0 \
		                -text "Debug" -width 8 \
		                -command "$this cb_launch_build"
	        pack $btns.launch -side top -padx 8 -pady 4 -fill x
		
		#Results
		set browser $this.list
		Browser& $browser\
			-exportselection y \
			-sort "" \
			-tabs "" \
			-filter_window 0 \
			-wrap word \
			-width $width \
			-height $height
		$browser tag config sel \
			-background $sn_options(def,select-bg)
		Editor& @@ set_tab $browser 2
		
		$browser bind <ButtonRelease-1> "$this handle_click"
		$browser bind <Return>   "$this handle_return"
		$browser bind <space>    "$this handle_return"
		$browser bind <Double-1> "$this handle_return"
		$browser bind <3> {make&_post_menu %W %X %Y ; break}

		#bind_focus_enter $browser {focus %W}
		pack $browser -side bottom -fill both -expand y

		Update_Layout
		
		#set project directory to the start directory
		global $this-startdir
		if {$sn_options(make-lastdir) != "" && [file isdirectory $sn_options(make-lastdir)]} {
			@@set $this-startdir [file nativename $sn_options(make-lastdir)]
		} else {
			@@set $this-startdir [file nativename $sn_options(sys,project-dir)]
		}

		global $this-makecommand
		@@set $this-makecommand [file nativename $sn_options(both,make-command)]

		upvar #0 $this-target target
		if {$target!="<External Makefile>"} {
			set b_target [snBuildTarget .tmptarget]
			$b_target LoadData $target
			[$entries.dir entry] delete 0 end
			[$entries.dir entry] insert 0 "[$b_target GetBuildDirectory]"
			$b_target delete

			# Disable the directory combobox so the
			# user can't edit the directory of the build target.
			$entries.dir.choose configure -state disabled
			$entries.dir config -state disabled
			[$entries.dir entry] configure -state disabled

		}
		
		SetTitle
		
		#call user defined function
		catch {sn_rc_make $this $browser}
		
		focus [$entries.entry entry]
	}
	destructor {
		#to be sure that the process is terminated
		catch {close $make_fd}
		upvar \#0 $this-target target
		paf_db_proj put IDEMakeCurrentTarget $target
		foreach v [@@info globals "$this-*"] {
			catch {uplevel #0 unset $v}
		}
	}
	method cget {config} {
	}
	method config {config} {
	}
	
	method handle_cancel {} {
		if {$cancelcommand != ""} {
			@@eval $cancelcommand
		}
	}
	
	method getstartdir {} {
		upvar #0 $this-startdir startdir
		return $startdir
	}
	
	method setmakecommand {cmd} {
		upvar #0 $this-makecommand makecommand
		@@set makecommand $cmd
		return $makecommand
	}

	# Call back if the debug or Run button is pressed.	
	method cb_launch_build {} {
		upvar #0 $this-target target

	        set exec_error ""

		if {$target=="<External Makefile>"} {
			# The users is building with a external Mkefile.
			# We should bring up the debug dialog.
			sn_debugger
		} else {
			# We need to find out if it's going to launch with
			# or without the debugger.
			set b_target [snBuildTarget .tmptarget]
			$b_target LoadData $target
			if {[$b_target GetLaunchWithDebugger]==1} {
				# We are launching with debugger.
				set runDir [$b_target GetBuildDirectory]
				set execCmd [file join $runDir [$b_target GetLaunchCommandLine]]
				sn_debugger $runDir \
					[file join $runDir $execCmd] \
					[$b_target GetDebugger]
			} else {
				global tcl_platform
				# Just run it stand alone.
				set runDir [$b_target GetBuildDirectory]
				set execCmd [$b_target GetLaunchCommandLine]
				catch {cd $runDir}
				if {$tcl_platform(os)=="Windows NT"} {
				    catch {exec cmd /c start $execCmd &} exec_error
				} elseif {$tcl_platform(os)=="Windows 95"} {
				    catch {exec command /c start $execCmd &} exec_error
				} else {
				    set execcmd "exec $execCmd"
				    catch {eval $execcmd} exec_error
				}
				catch {cd $sn_options(sys,project-dir)}
			}
			$b_target delete
		}
	        
	    if {$exec_error!=""} {
		sn_error_dialog $exec_error
	    }
	}

	# Call back if the selected "Build Target" is changed.
	method targets_cb {w} {
		upvar #0 $this-target target
		sn_log "IDE_DEBUG(Make& target_cb): target = $target "
		if {$target=="<External Makefile>"} {
			# Enable the directory combobox so the
			# user can edit it.
			$entries.dir.choose configure -state normal
			$entries.dir config -state normal
			[$entries.dir entry] configure -state normal

			# We will be launching with debugger.
			$btns.launch configure -text "Debug"
		} else {
			# enable the directory combobox
			# so we can edit it.
			$entries.dir.choose configure -state normal
			$entries.dir config -state normal
			[$entries.dir entry] configure -state normal

			# We need to find out if it's going to launch with
			# or without the debugger.
			set b_target [snBuildTarget .tmptarget]
			$b_target LoadData $target
			if {[$b_target GetLaunchWithDebugger]!=1} {
				# We are not using the debugger.
				$btns.launch configure -text "Run"
			} else {
				# We are launching with debugger.
				$btns.launch configure -text "Debug"
			}
			
			# Set the new directory in the combobox
			[$entries.dir entry] delete 0 end
			[$entries.dir entry] insert 0 "[$b_target GetBuildDirectory]"


			# Disable the directory combobox so the
			# user can't edit the directory of the build target.
			$entries.dir.choose configure -state disabled
			$entries.dir config -state disabled
			[$entries.dir entry] configure -state disabled
			
			# Be clean, delete the build target after ourselfs.
			$b_target delete
		}
		
	}

	
	# Update the targets combobox, debug/lunch and build dir.
		
	method RefreshTargetInfo {} {
		# get and set the targets list in the combobox.
		set targets_list [list "<External Makefile>"]
		eval lappend targets_list [GetTargetsList]
		$entries.targets contents $targets_list

		upvar #0 $this-target target

		if {$target!="<External Makefile>"} {
			# enable the directory combobox
			# so we can edit it.
			$entries.dir.choose configure -state normal
			$entries.dir config -state normal
			[$entries.dir entry] configure -state normal

			# We need to find out if it's going to launch with
			# or without the debugger.
			set b_target [snBuildTarget .tmptarget]
			$b_target LoadData $target
			if {[$b_target GetLaunchWithDebugger]!=1} {
				# We are not using the debugger.
				$btns.launch configure -text "Run"
			} else {
				# We are launching with debugger.
				$btns.launch configure -text "Debug"
			}
			
			# Set the new directory in the combobox
			[$entries.dir entry] delete 0 end
			[$entries.dir entry] insert 0 "[$b_target GetBuildDirectory]"


			# Disable the directory combobox so the
			# user can't edit the directory of the build target.
			$entries.dir.choose configure -state disabled
			$entries.dir config -state disabled
			[$entries.dir entry] configure -state disabled
			
			# Be clean, delete the build target after ourselfs.
			$b_target delete
		}
	}
	
	method ExportMakefile {} {
		upvar #0 $this-target target
		sn_log "IDE_DEBUG(Make& target_cb): target = $target "
		if {$target=="<External Makefile>"} {
			# We cannot generate a Makefile for this.
			bell
			return
		} else { 
			# Get the filename and path which will be
			# used to save the Makefile.
			set filename [tk_getSaveFile]
		
			if {$filename!=""} {	
				# Generate the Makefile
				set mkgen [MakefileGen .mkg_save_mkfile $target]
				set mkfile [$mkgen GenerateMakefile $filename]
				.mkg_save_mkfile delete
			}
		}
	}
	
	method ExecMake {{build_action ""}} {
		global sn_options
		upvar #0 $this-startdir startdir
		upvar #0 $this-makecommand makecommand
		upvar #0 $this-target target
				
		#make is running
		if {$Proceed != 0} {
			close_make
			return
		}
		
		#verify if all files are saved
		set modified 0
		foreach editor [itcl_info objects "*" -class Editor&] {
			if {[$editor cget -file_changed]} {
				set modified 1
			}
		}

		#ask to save changed files before running make
		if {$modified} {
			set answer [tk_dialog auto \
					[get_indep String MultiMake] \
					[get_indep String WantToFastSave] \
					error_image 0 \
					[get_indep String Yes] [get_indep String No] [get_indep String Cancel]]
			if {$answer == 2} {
				return
			}
			if {$answer == 0} {
				Editor& @@ SaveAll
			}
		}
		
		#add command and directory to history
		make_add_history $entries.entry $entries.dir

		# if user requires a make -k or make clean
		# doesn't get it from the GUI widget.
		if {$build_action==""} {
			#read make command
			get_make_cmd make_cmd
		} else {
			set make_cmd [file nativename $sn_options(both,make-command)]
		}

		# Check to see if we are executing a
		# users Makefile or an SN build target.
		if {$target!="<External Makefile>"} {

			# We are building an SN build target.
			set mkgen [MakefileGen .mkg $target]

			set mkfile [$mkgen GenerateMakefile]

			$mkgen delete
			set make_cmd "$make_cmd -f $mkfile"
			
			# If we are building an embedded target
			# we need to generate the .runcfg file.
		    	global sn_elix
			set b_target [snBuildTarget .tmptarget]
			$b_target LoadData $target
			set tool_chain [GetToolChainObject [$b_target GetToolChain]]
			if {$sn_elix && [$tool_chain GetIsEmbedded]} {
	    			global sn_options
				
				set prefix [file join [pwd] [$b_target GetBuildDirectory]]/
				set fd [open ${prefix}.runcfg w]
				set cmd [Elix& @@ make_config_command [$tool_chain GetShortName] \
						$prefix]
				puts $fd $cmd
				close $fd
			}
			$b_target delete
		}
		
		# Do we have to force rebuild
		if {$build_action=="forced"} {
			# Everything to be rebuilt.
			set make_cmd "$make_cmd -k"
		} elseif {$build_action=="clean"} {
			set make_cmd "$make_cmd clean"
		} else {
			set b_target [snBuildTarget .tmptarget]
			$b_target LoadData $target
			if {[$b_target GetNeedsRebuild]} {
				set make_cmd "$make_cmd clean all"
				$b_target SetNeedsRebuild 0
				$b_target SaveData
			}
			$b_target delete
		}

		#on window we must mask all "\"
		regsub -all {\\} $make_cmd {\\\\} make_cmd
		
		lappend make_cmd 2>@stdout


		#1. store last used make directory
		if {[file isdirectory $startdir]} {
			@@set sn_options(make-lastdir) $startdir
			
			#change to make directory
			catch {cd $startdir}
		}
		
		#2. start command
		sn_log "Executing make: $make_cmd"
		set ret [catch {@@set make_fd [@@open "| $make_cmd" r]} msg]
		
		#3. cd back to the project directory
		catch {cd $sn_options(sys,project-dir)}

		if {$ret} {
			sn_error_dialog "$make_cmd:\n$msg"
			return
		}
		@@fconfigure $make_fd -buffering line
		@@fconfigure $make_fd -encoding $sn_options(def,system-encoding)
		
		set browser_Configure [@@bind $browser.t <Configure>]
		@@bind $browser.t <Configure> { }
		pack $browser.y -side right -fill y -before $browser.t
			
		sn_log "dispatch_MakeEvent $this $browser $make_fd"

		@@fileevent $make_fd readable "dispatch_MakeEvent $this $browser $make_fd"
		
		handle_proceed disabled
	}
	
	method get_make_cmd {make_cmd} {
		global sn_options
		global env tcl_platform
		upvar #0 $this-makecommand makecommand
		upvar $make_cmd cmd
	
		#don't return it as a list element.
		set cmd $makecommand
		return $cmd
	}
	
	method close_make {{cancel ""}} {
		handle_proceed normal $cancel
		bell
	}

	#disable/enable input fields when the process is running/terminated
	method handle_proceed {state {cancel ""}} {
		upvar #0 $this-ButtonPressed pressed
		
		@@set pressed 0
		
		# Save off the widget states to be restored later.
		# They might be set for a certain reason.
		set ent_state [[$entries.entry entry] cget -state]
		set dir_state [[$entries.dir   entry] cget -state]

		if {$state == "normal"} {
			catch {close $make_fd}
			$btns.start  config -state normal
			$btns.cancel config -state disabled
			
			#rebind configure event
			@@bind $browser.t <Configure> $browser_Configure
			$browser.t config -state normal
			if {$cancel == "cancel"} {
				set endstr "*** Canceled ***\n"
			} else {
				set endstr "+++ End +++\n"
			}
			$browser.t insert end $endstr
			$browser.t config -state disabled
			
			set Proceed 0
		} else {
			set Proceed 1
			
			$browser.t config -state normal
			$browser.t delete 0.0 end
			$browser.t config -state disabled
			
			$btns.start  config -state disabled
			$btns.cancel config -state normal
		}
		Browser& @@ resize_widget $browser.t
		
		# Restore widget states.
		[$entries.entry entry] config -state $ent_state
		[$entries.dir   entry] config -state $dir_state
	}

	method next_entry {amount} {
		set widget $browser.t
		
		$browser tag remove sel 0.0 end
		$browser tag add sel "active${amount}line linestart" "active${amount}line lineend + 1c"
		
		$widget tag remove activetag 0.0 end
		$widget tag add activetag "active${amount}line linestart" "active${amount}line lineend"
		$widget mark set active "active${amount}line linestart"
		$widget mark set insert active
		$widget see active
		
		if {$selectcommand != ""} {
			watch y $this
			@@eval $selectcommand [Selection]
			watch n $this
		} else {
			handle_return
		}
		upvar #0 $this-ButtonPressed pressed
		@@set pressed 1
	}
	
	method Title {{full 1}} {
		global sn_options
		set t [string trimright [get_indep String MultiMake] "."]
		set txt [[$entries.entry entry] get]
		if {$txt != ""} {
			set t "$t: $txt"
		}
		if {$full} {
			return [sn_title $t]
		} else {
			return $t
		}
	}
	method Icon {} {
		return [sn_view_icon [get_indep String MultiMake]]
	}
	
	method SetTitle {} {
		#it can happen, that the string contains unmatched brackets
		#and Tcl respones with error message.
		catch {$topw title [Title]}
		$topw iconname [Icon]
	}
	
	method handle_return {} {
		$browser clear_search_pat
		if {[$browser index active] != ""} {
			$browser tag remove sel 0.0 end
			$browser tag add sel {active linestart} {active lineend + 1c}
			set res [sn_grep_edit_file [$browser get {active linestart} {active lineend}]]
			if {$res == "" && !$hold} {
				catch {destroy $topw}
			}
		}
		upvar #0 $this-ButtonPressed pressed
		@@set pressed 1
	}
	
	#Click can goto a symbol
	method handle_click {} {
		if {$selectcommand != ""} {
			watch y $this
			@@eval $selectcommand [Selection]
			watch n $this
		}
		upvar #0 $this-ButtonPressed pressed
		@@set pressed 1
	}
	
	method Split_Line {} {
		global sn_options
	
		set rng [$browser.t tag ranges sel]
		if {$rng == ""} {
			return ""
		}
		set line [eval $browser.t get $rng]
		if {$line == ""} {
			return
		}
		set pars [sn_goto_comp_error_or_grep $line]
		if {$pars == ""} {
			return ""
		}
		set file [lindex $pars 0]
		set pos [lindex $pars 1]
		set file [sn_convert_FileName $file]
		
		return [list $file $pos]
	}
	
	method filter {{all 0}} {
	}
	
	method activate {} {
		if {$symbols != "" && ($next == "" || [$next whoami] != "edit")} {
			#delete old contents
			$symbols contents ""
			$symbols selecttext ""
			$symbols disable
		}
		if {$menu != ""} {
		}
		if {$toolbar != ""} {
			pack $toolbar.makefr -side left
		}
	}
	
	method deactivate {} {
		if {$symbols != "" && ($next == "" || [$next whoami] != "edit")} {
			$symbols disable
		}
		if {$menu != ""} {
		}
		if {$toolbar != ""} {
			pack forget $toolbar.makefr
		}
	}
	
	#this function is called, when the symbols combobox
	#is called
	method postcommand {m} {
	}

	method Selection {} {
		global tcl_platform

		set sel [Split_Line]
		if {$sel != ""} {
			set file [lindex $sel 0]
			set line [lindex $sel 1]
			if {$tcl_platform(platform) != "windows"} {
				set last [string last "/" $file]
				set len  [string length $file]
				#sometimes we get strang results from 'sn_goto_comp_error_or_grep'
				if {$last != -1 && [expr $last + 1] == $len || [string first "**" $file] == 0} {
					set file ""
					set line ""
				}
			}
		} else {
			set file ""
			set line ""
		}
		return [list "" "" "" $file $line "" "" ""]
	}
	
	method gotosymbol {{scope ""} {sym ""} {cls ""} {file ""} {from ""} {type ""} {prm ""} {to ""} {always 1}} {
		if {$sym != ""} {
			#convert string to be used for make
			regsub -all {\^|\\|\.|\*|\$|\-|\[|\]} $sym {\\&} sym
			regsub -all {\<|\>} $sym {[&]} sym
		}
		return 1
	}
	
	method clearselection {} {
		$browser clear
	}
	
	method Update_Layout {} {
		global sn_options
		
		$browser.t config \
				-font $sn_options(def,default-font) \
				-selectforeground $sn_options(def,select-fg) \
				-selectbackground $sn_options(def,select-bg)
	}
	
	method Focus {} {
		focus $browser.t
	}

	#make a title for the Dump position, this is usefull
	#for the views stack (prev/next)
	method DumpTitle {{dump ""}} {
		if {$dump == ""} {
			set dump [Dump]
		}
		set scp [lindex $dump 0]
		set sym [lindex $dump 1]
		set cls [lindex $dump 2]
		set name [string trim "${sym}($scp) $cls"]
		return "Make $name"
	}

	method AddHistoryFromDump {dump title} {
	}
	
	#return the important data to restore this widget
	#in later time again (used by saving the project)
	method Dump {} {
		return [Selection]
	}
	
	#gets the result from the function "Dump" to
	#restore the older state (used by restoring the project)
	method Restore {str} {
		eval $this gotosymbol $str
		#Focus
	}
	
	method Close {{mode 0}} {
		return 1
	}
	
	method whoami {} {
		return make
	}
	method next {} {
		return $next
	}
	
	#if an editor is added to the make, call the
	#goto function of the editor
	method goto {combo txt} {
		if {$next != "" && [$next whoami] == "edit"} {
		
			#dump the current view into the history stack
			$topw history_stack_add_point $next
		
			$next goto $combo $txt
		}
	}
	
	method make_select_command {combo txt} {
		global sn_options
		global $this-makecommand
		global $this-startdir
		
		#set i [[$combo treew] index active]
		set i [[$combo treew] search -exact -- $txt 0]

		if {$i != ""} {
			set txt [lindex $sn_options(make_history_cmd) $i]
			@@set $this-makecommand [lindex $txt 0]
			@@set $this-startdir    [lindex $txt 1]
		}
	}
	
	#command history with directory
	proc make_history_moveup {cmdw cmddir} {
		global sn_options
		
		#add pattern into the history stack
		sn_add_to_histroy_stack \
				sn_options(make_history_cmd) \
				$cmddir
		
		#add the new history to the combo box
		set elms ""
		foreach el $sn_options(make_history_cmd) {
			lappend elms "[lindex $el 0] <[lindex $el end]>"
		}
		
		$cmdw contents $elms
	}
	
	proc make_add_history {cmdw dirw} {
		global sn_options
		
		set cmd [$cmdw getentry]
		set dir [$dirw getentry]
		
		#command history with directory
		if {$cmd != ""} {
			set cmddir [list $cmd $dir]
			sn_add_to_histroy_stack \
					sn_options(make_history_cmd) \
					$cmddir
		}
		#add the new history to the combo box
		set elms ""
		foreach el $sn_options(make_history_cmd) {
			lappend elms "[lindex $el 0] <[lindex $el end]>"
		}
		$cmdw contents $elms
		
		#directory list
		if {$dir != "" && [file isdir $dir]} {
			sn_add_to_histroy_stack \
					sn_options(make_history_dir) \
					$dir
		}
		$dirw contents $sn_options(make_history_dir)
	}

	protected make_fd ""
	protected Proceed 0
	protected topw "."
	protected entries ""
	protected btns ""
	protected browser ""
	protected browser_Configure " "

	#don't close window after selecting an entry
	public hold 1
	public width 80
	public height 10
	public symbols ""
	public symbols_filter ""
	public mesg_area ""
	public mesg_linenumber ""
	public menu ""
	public toolbar ""
	public selectcommand ""
	public doubleclickcommand ""
	public cancelcommand ""
	
	#contains the next widget, when the window is spliten
	#into more than one area
	#Simulate a single chain
	public next ""
}

proc dispatch_MakeEvent {cls browser fd} {
	upvar #0 $cls-ButtonPressed pressed

	if {[catch {set res [gets ${fd} line]} err]} {
		sn_log "caught Make& pipe error \"$err\""
		catch {$cls close_make}
		return
	}
	if {${res} == -1 && [string length $line] == 0 && ![eof ${fd}]} {
		# This means a read returned no data but the channel is
		# not at EOF, this can happen when cmd prints to stderr
		# handle this case by simply doing nothing.
		return
	} elseif {${res} > 0} {
		$browser.t config -state normal
		$browser.t insert end "$line\n"
		$browser.t config -state disabled
		if {!$pressed} {
			$browser.t see active
		}
	} elseif {$res < 0} {
		catch {$cls close_make}
	}
	update idletasks
}

#returns the object file of a source file
proc sn_object_file {file} {
	global tcl_platform

	set i [string last "." $file]
	if {$tcl_platform(platform) == "windows"} {
		set obj "obj"
	} else {
		set obj "o"
	}
	#file contains no extension
	if {$i < 1} {
		return $file.$obj
	}
	return [string range $file 0 $i]$obj
}

proc sn_make {} {
	global sn_options
	global tkeWinNumber
	
	set s [find_reusable_window "make-"]
	if {$s == ""} {
		incr tkeWinNumber
		set s ".sn-make-$tkeWinNumber"
		sn_create_window $s 1
		
		Make& $s.make \
			-symbols "" \
			-symbols_filter "" \
			-menu "" \
			-toolbar $s.exp \
			-mesg_area $s.status.msg
		pack $s.make -expand y -fill both -side left
		$s move_to_mouse
	        set menu [$s cget -menu]

	        set toolmenu [menu .make_tool_menu$tkeWinNumber -tearoff 0]
	        $menu insert 3 cascade -menu $toolmenu -label "Tools"
	        add_make_tools_menu $toolmenu $s.make
	} else {
		$s raise
	}
	
	return $s
}

proc add_make_tools_menu { tools_menu w_make} {
    $tools_menu insert 10 command -label "Build" -command "$w_make ExecMake"
    $tools_menu insert 11 command -label "Force Build" -command "$w_make ExecMake forced"
    $tools_menu insert 12 command -label "Clean Build" -command "$w_make ExecMake clean"
    $tools_menu add separator
    $tools_menu add command -label "Export Makefile..." -command "$w_make ExportMakefile"
}

# This method will be invoked when the user right clicks
# in the output window. This code is copied from the
# browser class. This is a really sucky way to extend
# a widget but we need to ship the product!

proc make&_post_menu {w x y} {
	set m .sn_pop_menu_listbox
	# It has to be destroyed because we might have problems with "tk_popup"!
	catch {tk_destroy $m}
	menu $m -tearoff 0\
		-postcommand "make&_post_menu_update $m $w"
	wm overrideredirect $m 1
	tk_popup $m $x $y
}

# This will be invoked when the menu is actually ready to display

proc make&_post_menu_update {m w} {
	$m delete 0 end

	$m add command -label [get_indep String SaveBufferAs] \
		-command "make&_respond_to_save_as $m $w"
}

# This will be called if they choose "save as" from right click menu

proc make&_respond_to_save_as { m w } {
	global sn_options
	
	#puts "Save As click"
	
	# Get a file name to save the buffer as!
	set file [tk_getSaveFile -initialfile build.out]
	
	# They clicked Cancel
	if {$file == ""} {
		return
	}
		
	# write the contents of the widget into the file

	if {[catch {open $file w} fd]} {
		sn_error_dialog "$fd"
		return
	}
	
	fconfigure $fd -encoding $sn_options(def,system-encoding)

	puts $fd [$w get 0.0 end]
	
	close $fd
}
