1# e93 base startup file
2# This is the top of the startup script tree for e93
3# it finds this file, which in turn includes all other
4# files which are executed at startup time.
5
6# NOTE: normally, this file requires no modifications,
7# since all of its functionality can be overridden in
8# other files.
9
10# WARNING: be careful when editing this file,
11# e93 will stop processing this file at the FIRST error!
12
13# Globals/Defaults
14
15set			tcl_precision			16;					# increase precision
16
17set			sysPrefsDir				"[file dirname $SCRIPTPATH]";	# place where system preferences are stored
18set			userPrefsDir			"~/.e93";			# place where user's preferences are stored
19
20set			mapsDir					"syntaxmaps";			# place inside prefs directory where syntaxmaps are stored
21set			auxMapsDir				"syntaxmaps_aux";		# user can add additional maps in here
22set			highlightDir			"highlightschemes";		# place inside prefs directory where highlighting schemes are stored
23set			auxHighlightDir			"highlightschemes_aux";	# user can add additional highlight schemes in here
24set			modulesDir				"modules";				# place inside prefs directory where modules are stored
25set			auxModulesDir			"modules_aux";			# user can add additional modules in here
26set			imagesDir				"images";				# all images the editor uses are read from here
27set			prefsDir				"prefs";				# place where user's preferences can be written
28
29set			untitledCounter			0;					# used to give each new untitled window a unique name
30set			lineWrap				0;					# no line wrapping currently
31set			lineWrapColumn			70;					# column where lines should be wrapped
32
33set			windowWidthLimit		1400;				# width in pixels that we hold new windows to on really large screens
34
35set			grepFilesChoice			"";					# initialize the data in the grep files dialog
36set			replaceSelectionsChoice	"";					# initialize the data in the replace selections dialog
37set			pipeSelectionsChoice	"";					# initialize the data in the pipe selections dialog
38set			mailRecipientChoice		"";					# initialize the data in the mail recipient dialog
39set			mailSubjectChoice		"";					# initialize the data in the mail subject dialog
40set			refillLimitChoice		"70";				# initialize the data in the refill limit dialog
41
42#scrollbarplacement	left;								# X-specific command to set default placement for vertical scroll bars (defaults to right)
43
44# Set the default fonts to use
45setdefaultviewfont			{-*-lucidatypewriter-medium-r-normal-sans-12-*-*-*-*-*-*-*};	# font for viewing text
46setdefaultmenufont			{-*-new century schoolbook-bold-r-normal--14-*-*-*-*-*-*-*};	# font for the menus
47setdefaultdialogbuttonfont	{-*-new century schoolbook-bold-r-normal--14-*-*-*-*-*-*-*};	# font for dialog buttons
48setdefaultdialogtextfont	{-*-new century schoolbook-bold-r-normal--14-*-*-*-*-*-*-*};	# font for dialog items
49setdefaultstatusbarfont		{-*-lucidatypewriter-medium-r-normal-sans-12-*-*-*-*-*-*-*};	# font for the status bar
50
51# Set the characters considered parts of words when double clicking.
52setwordchars {ABCDEFGHJIKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@_}
53
54# Set checkboxes in the search dialog to default values.
55# Change these to have them default whatever way you would like when e93 starts.
56set			sd_backwards			0;					# do not search backwards by default at startup
57set			sd_wrapAround			0;					# do not wrap around by default at startup
58set			sd_selectionExpression	0;					# do not use selection expressions by default at startup
59set			sd_ignoreCase			0;					# do not ignore case by default
60set			sd_limitScope			0;					# do not limit scope by default
61set			sd_replaceProc			0;					# do not treat replace text as procedure
62
63# set up default command used to print text in "buffer"
64#set		defaultPrintCommand		"a2ps -Pps18 --stdin=\$buffer -T\[gettabsize \$buffer\]"
65#set		defaultPrintCommand		"lpr -#1 -J=\$buffer"
66set			defaultPrintCommand		"enscript -B -h -c -T\[gettabsize \$buffer\] -f Courier7"
67
68
69# if e93 is unable to match a document name, these will be used as the defaults
70set			defaultTabSize			4
71set			defaultColorScheme		"e93";				# these must exist, or e93 will complain
72set			defaultSyntaxMap		"unknown";			# by default, we don't know what kind of document it is
73
74
75# Styles enumerations for syntax coloring rules
76# These style numbers are arbitrary (except for style_default)
77set			style_default			0;					# define some standard styles to make life easier
78set			style_comment			1;
79set			style_string			2;
80set			style_char				3;
81set			style_digit				4;
82set			style_operator			5;
83set			style_variable			6;
84set			style_value				7;
85set			style_delimiter			8;
86set			style_keyword			9;
87set			style_type				10;
88set			style_directive			11;
89set			style_function			12;
90set			style_label				13;
91set			style_local_label		14;
92set			style_bold				15;
93set			style_indent0			16;
94set			style_indent1			17;
95set			style_indent2			18;
96set			style_indent3			19;
97set			style_indent4			20;
98set			lastStyle				$style_indent4;	# set this to allow others to add more styles at the end
99
100# define coloring rules for various languages
101
102# see if the user has his own set of maps specified, if so, skip including the defaults, and get only his
103if {[file exists [file join $userPrefsDir $mapsDir]]} \
104{
105	foreach file [lsort [glob -nocomplain [file join $userPrefsDir $mapsDir *.tcl]]] \
106	{
107		source $file;
108	}
109} \
110else \
111{
112	# use all system defined syntax maps
113	foreach file [lsort [glob -nocomplain [file join $sysPrefsDir $mapsDir *.tcl]]] \
114	{
115		source $file;
116	}
117}
118
119# get user's additional syntax maps (if any)
120if {[file exists [file join $userPrefsDir $auxMapsDir]]} \
121{
122	foreach file [lsort [glob -nocomplain [file join $userPrefsDir $auxMapsDir *.tcl]]] \
123	{
124		source $file;
125	}
126}
127
128# see if the user has his own set of highlight schemes specified, if so, skip including the defaults, and get only his
129if {[file exists [file join $userPrefsDir $highlightDir]]} \
130{
131	foreach file [lsort [glob -nocomplain [file join $userPrefsDir $highlightDir *.tcl]]] \
132	{
133		source $file;
134	}
135} \
136else \
137{
138	# use all system defined schemes
139	foreach file [lsort [glob -nocomplain [file join $sysPrefsDir $highlightDir *.tcl]]] \
140	{
141		source $file;
142	}
143}
144
145# get user's additional highlighting schemes (if any)
146if {[file exists [file join $userPrefsDir $auxHighlightDir]]} \
147{
148	foreach file [lsort [glob -nocomplain [file join $userPrefsDir $auxHighlightDir *.tcl]]] \
149	{
150		source $file;
151	}
152}
153
154# Tcl console
155newbuffer tclConsole; setbuffervariable tclConsole lowpriority "";		# create a buffer for Tcl's stdout
156#settclstdout tclConsole;	# direct the messages there
157#settclstderr tclConsole;	# do not direct errors here by default, since they override the editor's stderr and cause it not to output
158
159# Clipboard buffers
160newbuffer	clip0; setbuffervariable clip0 lowpriority "";				# set the lowpriority variable which tells us to treat windows on this buffer specially
161newbuffer	clip1; setbuffervariable clip1 lowpriority "";
162newbuffer	clip2; setbuffervariable clip2 lowpriority "";
163newbuffer	clip3; setbuffervariable clip3 lowpriority "";
164newbuffer	clip4; setbuffervariable clip4 lowpriority "";
165newbuffer	clip5; setbuffervariable clip5 lowpriority "";
166newbuffer	clip6; setbuffervariable clip6 lowpriority "";
167newbuffer	clip7; setbuffervariable clip7 lowpriority "";
168newbuffer	clip8; setbuffervariable clip8 lowpriority "";
169newbuffer	clip9; setbuffervariable clip9 lowpriority "";
170
171setclipboard clip0;		# set this as the current clipboard
172
173# Search buffers
174newbuffer	findBuffer; setbuffervariable findBuffer lowpriority "";				# create default buffer to use for "find"
175newbuffer	replaceBuffer; setbuffervariable replaceBuffer lowpriority "";			# create default buffer to use for "replace"
176newbuffer	tempFindBuffer; setbuffervariable tempFindBuffer lowpriority "";		# these buffers are for commands which are implemented with search/replace, but do not want to overwrite the find/replaceBuffers
177newbuffer	tempReplaceBuffer; setbuffervariable tempReplaceBuffer lowpriority "";
178
179
180# ---------------------------------------------------------------------------------------------------------------------------------
181
182# Determine screen and window sizes.
183set			screenWidth				[lindex [screensize] 0]
184set			screenHeight			[lindex [screensize] 1]
185set			windowHeight			[expr $screenHeight*3/4];	# set the initial default height for new windows
186set			windowWidth				[expr $windowHeight*4/3];	# set the initial default width for new windows (make 4:3 based on width)
187
188# Limit the width on really large screens, since at some point, extra width isn't
189# getting you much.
190if {$windowWidth>$windowWidthLimit} \
191{
192	set			windowWidth			$windowWidthLimit;
193}
194
195# Determine how windows stagger when they are opened.
196set			staggerInitialX			[expr $screenWidth/24];		# set the initial default X position for new windows
197set			staggerInitialY			[expr $screenHeight/16];	# set the initial default Y position for new windows
198set			staggerIncrementX		[expr $screenWidth/160];	# amount to increment by
199set			staggerIncrementY		[expr $screenHeight/160];
200set			staggerMaxX				[expr $screenWidth*3/24]
201set			staggerOpenX			$staggerInitialX;			# set the current X position for new windows
202set			staggerOpenY			$staggerInitialY;			# set the current Y position for new windows
203
204# ---------------------------------------------------------------------------------------------------------------------------------
205# Procedures
206
207# Convenience function for setting marks in buffers
208# this will reset the mark if it already exists, and create it if
209# it does not already exist
210proc setmark {buffer mark} \
211{
212	catch {newmark $buffer $mark};								# create new mark if none existed already
213	copyselection $buffer -destmark $mark;						# copy selection into it
214}
215
216# Convenience function for locating marks in buffers
217# this will reset the mark if it already exists, and create it if
218# it does not already exist
219proc gotomark {buffer mark} \
220{
221	copyselection $buffer -sourcemark $mark;					# copy the mark over the buffer's selection
222}
223
224# Home a window to the start of selection (with arguments)
225proc HomeWindowToSelectionStart {window args} \
226{
227	set position [lindex [getselectionends $window] 0];			# get selection start position
228	eval [list homewindow $window] $position $position $args;	# home to start
229}
230
231# Return true if the passed window name is one of the low-priority windows.
232# A low priority window is one that e93 should not ask to save, even if it
233# has been modified...
234proc LowPriority {window} \
235{
236	expr {[catch {getbuffervariable $window lowpriority}]==0}
237}
238
239# When the interpreter does not understand a given command, send it to a shell.
240# NOTE: no interactive commands can be executed this way, because Tcl would
241# be suspended (waiting for the interactive command to finish). If Tcl is
242# suspended, then e93 is also suspended (waiting for the Tcl command to finish)
243# So there is no way that e93 could provide input to an interactive command.
244# Interactive commands should be run as tasks.
245#proc unknown args \
246#{
247#	return [uplevel exec $args]
248#}
249
250# NOTE: the code above has been replaced by this:
251proc console {} {}
252set tcl_interactive 1
253# those 2 lines tell the tcl built-in unknown routine to behave as
254# desired, and also get us auto loading and other useful features
255
256
257# If there is no selection, select the current line
258proc SelectLineWhenNoSelection {buffer} \
259{
260	set ends [getselectionends $buffer]
261	set start [lindex $ends 0]
262	set end [lindex $ends 1]
263	if {$start==$end} \
264	{
265		selectline $buffer [lindex [positiontolineoffset $buffer $start] 0];	# select the line the cursor is on
266	}
267}
268
269# Copy the selection in buffer to the clipboard passed.
270# If there is no selection in buffer, then select the line the cursor is on.
271proc SmartCopy {buffer clipboard} \
272{
273	SelectLineWhenNoSelection $buffer
274	copy $buffer $clipboard
275}
276
277# Cut the selection in buffer to the clipboard passed.
278# If there is no selection in buffer, then select the line the cursor is on.
279proc SmartCut {buffer clipboard} \
280{
281	SelectLineWhenNoSelection $buffer
282	cut $buffer $clipboard
283}
284
285# Replace the entire contents of a buffer with the passed text, and clear undos on the buffer.
286# NOTE: it would be bad if this were called on a document that had been edited for hours and not
287# saved! -- It would blow away the entire contents.
288proc TextToBuffer {buffer text} \
289{
290	selectall $buffer;								# select everything, so insert will remove it
291	insert $buffer $text;							# write over everything in buffer
292	flushundos $buffer;								# get rid of any undo information for this buffer
293}
294
295# Move the stagger position to the next spot for a new staggered window.
296proc UpdateStaggerPosition {} \
297{
298	uplevel #0 \
299	{
300		incr staggerOpenX $staggerIncrementX;
301		incr staggerOpenY $staggerIncrementY;
302		if {$staggerOpenX>$staggerMaxX} \
303		{
304			set staggerOpenX $staggerInitialX;
305		 	set staggerOpenY $staggerInitialY
306		}
307	}
308}
309
310# Tile the windows on the display.
311proc TileWindows {} \
312{
313	set horizontalNumber 4;								# number of windows we want across
314	set verticalNumber 4;								# number we want down
315	set horizontalBorder 30;							# number of pixels to leave around edges of screen
316	set verticalBorder 30;
317	set screendimensions [screensize]
318	set width [lindex $screendimensions 0]
319	set height [lindex $screendimensions 1]
320	set windowWidth [expr ($width-($horizontalBorder*2))/$horizontalNumber];	# get the width of each individual window
321	set windowHeight [expr ($height-($verticalBorder*2))/$verticalNumber];		# get the height of each individual window
322	set horizontalIndex 0; set verticalIndex 0;			# init counters that tell us where we are
323	foreach window [windowlist] \
324	{
325		if {![LowPriority $window]} \
326		{
327			setrect $window [expr $horizontalBorder+$horizontalIndex*$windowWidth] [expr $verticalBorder+$verticalIndex*$windowHeight] $windowWidth $windowHeight
328			incr horizontalIndex
329			if {$horizontalIndex>=$horizontalNumber} \
330			{
331				set horizontalIndex 0;
332				incr verticalIndex;
333				if {$verticalIndex>=$verticalNumber} \
334				{
335					set verticalIndex 0;
336				}
337			}
338		}
339	}
340}
341
342# Stack the windows on the display.
343proc StackWindows {} \
344{
345	global staggerInitialX staggerInitialY staggerOpenX staggerOpenY windowWidth windowHeight
346
347	set staggerOpenX $staggerInitialX;					# set the initial default X
348	set staggerOpenY $staggerInitialY;					# set the initial default Y
349	foreach window [windowlist] \
350	{
351		if {![LowPriority $window]} \
352		{
353			setrect $window $staggerOpenX $staggerOpenY $windowWidth $windowHeight
354			UpdateStaggerPosition
355		}
356	}
357}
358
359# return the foreground color for the given index of the given color scheme
360proc HighlightSchemeForegroundColor {schemeName styleIndex} \
361{
362	global HighlightSchemes
363	set schemeArray $HighlightSchemes($schemeName);	# get the name of the array which holds the scheme information
364	global $schemeArray
365
366	set elementValue [lindex [array get $schemeArray $styleIndex] 1]
367	return [lindex $elementValue 0]
368}
369
370# return the background color for the given index of the given color scheme
371proc HighlightSchemeBackgroundColor {schemeName styleIndex} \
372{
373	global HighlightSchemes
374	set schemeArray $HighlightSchemes($schemeName);
375	global $schemeArray
376
377	set elementValue [lindex [array get $schemeArray $styleIndex] 1]
378	return [lindex $elementValue 1]
379}
380
381# return the font name for the given index of the given color scheme
382proc HighlightSchemeFont {schemeName styleIndex} \
383{
384	global HighlightSchemes
385	set schemeArray $HighlightSchemes($schemeName);
386	global $schemeArray
387
388	set elementValue [lindex [array get $schemeArray $styleIndex] 1]
389	return [lindex $elementValue 2]
390}
391
392# Given a window, set the highlight scheme (which colors and fonts to use)
393proc SetHighlightScheme {window schemeName} \
394{
395	global HighlightSchemes
396	set schemeArray $HighlightSchemes($schemeName);	# get the name of the array which holds the scheme information
397	global $schemeArray
398	foreach element [array names $schemeArray] \
399	{
400		set elementValue [lindex [array get $schemeArray $element] 1]
401		if {$element=="selection"} \
402		{
403			setselectioncolors $window [lindex $elementValue 0] [lindex $elementValue 1]
404		} \
405		else \
406		{
407			setcolors $window [lindex $elementValue 0] [lindex $elementValue 1] $element
408			setfont   $window [lindex $elementValue 2] $element
409		}
410	}
411}
412
413# Given a buffer, set the highlight mode (which syntax to parse)
414proc SetHighlightMode {buffer highlightModeName} \
415{
416	setsyntaxmap $buffer $highlightModeName
417}
418
419# work out the default color scheme, highlight mode, and tab size for buffer
420# return a list which contains a boolean that tells if something other than the
421# default is being returned, the tab size, the color scheme name, and the syntax map name
422proc GetDefaultStyleInfo {buffer} \
423{
424	global extensionHuntExpression extensionTabSize extensionColorScheme extensionMapName defaultTabSize defaultColorScheme defaultSyntaxMap
425
426	set hadMatch 0
427
428	# set up defaults
429	set tabSize $defaultTabSize
430	set schemeName $defaultColorScheme
431	set mapName $defaultSyntaxMap
432
433	foreach extensionAttrName [array names extensionHuntExpression] \
434	{
435		if {[regexp $extensionHuntExpression($extensionAttrName) [file tail $buffer]]} \
436		{
437			set tabSize $extensionTabSize($extensionAttrName)
438			set schemeName $extensionColorScheme($extensionAttrName)
439			set mapName $extensionMapName($extensionAttrName)
440			set hadMatch 1
441			break
442		}
443	}
444	lappend temp $hadMatch $tabSize $schemeName $mapName;		# return the 3 items
445}
446
447# when a window's name has been changed, this is called to update the
448# styles in use for the window
449# if the name is recognized, this will change the style, otherwise it
450# does nothing
451proc UpdateStyle {window} \
452{
453	set temp [GetDefaultStyleInfo $window]
454	if {[lindex $temp 0]} \
455	{
456		settabsize $window [lindex $temp 1]
457		SetHighlightScheme $window [lindex $temp 2]
458		SetHighlightMode $window [lindex $temp 3]
459	}
460}
461
462# Create a window onto buffer, using given values for position, size, font, tabSize, color, and highlighting.
463proc OpenWindow {buffer x y width height tabSize schemeName mapName} \
464{
465	global style_default
466
467	if {[haswindow $buffer]} \
468	{
469		settopwindow $buffer;						# if it was already open, then just put it to the top, change nothing
470	} \
471	else \
472	{
473		openwindow $buffer $x $y $width $height [HighlightSchemeFont $schemeName $style_default] $tabSize [HighlightSchemeForegroundColor $schemeName $style_default] [HighlightSchemeBackgroundColor $schemeName $style_default]
474		SetHighlightScheme $buffer $schemeName
475		SetHighlightMode $buffer $mapName
476		HomeWindowToSelectionStart $buffer;			# go to the cursor/selection
477	}
478}
479
480# Create a window onto buffer, using default values for position and size
481# and given values for, font, tabSize, color, and highlighting.
482proc OpenStaggeredWindow {buffer tabSize schemeName mapName} \
483{
484	global staggerOpenX staggerOpenY screenWidth screenHeight windowWidth windowHeight
485
486	if {[haswindow $buffer]} \
487	{
488		settopwindow $buffer;						# if it was already open, then just put it to the top
489	} \
490	else \
491	{
492		if {[LowPriority $buffer]} \
493		{
494			set width [expr $screenWidth*3/4]
495			set height [expr $screenHeight*1/8]
496			set x [expr ($screenWidth-$width)/2]
497			set y [expr $screenHeight-$height]
498		} \
499		else \
500		{
501			set width $windowWidth
502			set height $windowHeight
503			set x $staggerOpenX
504			set y $staggerOpenY
505			UpdateStaggerPosition;
506		}
507		OpenWindow $buffer $x $y $width $height $tabSize $schemeName $mapName
508	}
509}
510
511# Create a window onto buffer, using default values for position, size, font, tabSize, color, and highlighting.
512proc OpenDefaultWindow {buffer} \
513{
514	if {[haswindow $buffer]} \
515	{
516		settopwindow $buffer;						# if it was already open, then just put it to the top
517	} \
518	else \
519	{
520		if {[LowPriority $buffer]} \
521		{
522			set tabSize 4
523			set schemeName "Low Priority"
524			set mapName ""
525		} \
526		else \
527		{
528			set temp [GetDefaultStyleInfo $buffer]
529			set tabSize [lindex $temp 1]
530			set schemeName [lindex $temp 2]
531			set mapName [lindex $temp 3]
532		}
533		OpenStaggeredWindow $buffer $tabSize $schemeName $mapName
534	}
535}
536
537# Return a good name for a new window.
538proc NewWindowName {} \
539{
540	global untitledCounter;							# reference the global
541	set name "Untitled-$untitledCounter";			# get new name
542	incr untitledCounter;							# next time, make the count one larger
543	return $name;									# return the name of the new window
544}
545
546# Create a new window with a default name, return the name.
547proc NewWindow {} \
548{
549	OpenDefaultWindow [set name [newbuffer [NewWindowName]]];	# open a new window with new name
550	return $name;									# return the name of the new window
551}
552
553# Open the passed file name into a buffer, and record the modification time
554# of the file into the variable "mtime" attached to the buffer
555# if there is a problem, fail just like openbuffer would
556proc OpenBufferRecordMtime {file} \
557{
558	if {[catch {openbuffer $file} message]==0} \
559	{
560		# see if there is ALREADY a modification time tag associated with this file, if so, check against current mtime and complain if they differ
561		# NOTE: there could already be a variable assigned if the file was open already
562		if {[catch {getbuffervariable $message mtime} oldmtime]==0} \
563		{
564			if {[catch {file mtime $message} newmtime]==0} \
565			{
566				if {$oldmtime!=$newmtime} \
567				{
568					okdialog "WARNING!\n\nIt looks like another application modified:\n$message\nwhile it was open for editing.\n"
569				}
570			} \
571			else \
572			{
573				okdialog "WARNING!\n\nA buffer exists for the requested file:\n$message\nBut could not read mtime.\n"
574			}
575		} \
576		else \
577		{
578			catch {setbuffervariable $message mtime [file mtime $message]};
579		}
580		return $message;
581	} \
582	else \
583	{
584		return -code error $message;
585	}
586}
587
588# Attempt to open a list of files one at a time.
589# If any fails to open, report why, and give the user a chance to cancel.
590proc OpenList {list} \
591{
592	set numItems [llength $list]
593	set item 0
594	while {$item<$numItems} \
595	{
596		set file [lindex $list $item]
597		incr item
598		if {[catch {OpenDefaultWindow [OpenBufferRecordMtime $file]} errorMessage]!=0} \
599		{
600			if {$item==$numItems} \
601			{
602				okdialog "Failed to open '$file'\n$errorMessage"
603			} \
604			else \
605			{
606				okcanceldialog "Failed to open '$file'\n$errorMessage\n\nContinue?"
607			}
608		}
609	}
610}
611
612# Given an "filename" from a list, try to interpret it as the output from
613# an GNU tool:
614# filename:nnnn
615# If it looks like that, then extract the filename and line number
616# and return them in a list, otherwise return an error
617proc SmartOpenListGNU {listItem} \
618{
619	if {[regexp {^([^:]+):([0-9]+)} $listItem whole tempFile tempLine]} \
620	{
621		if {[file exists $tempFile]} \
622		{
623			lappend temp $tempFile $tempLine;	# make a list of 2 elements (filename and line number)
624			lappend temp2 $temp;				# return list of lists
625			return $temp2;
626		}
627	}
628	return -code error;
629}
630
631# Given an "filename" from a list, try to interpret it as the output from
632# an MPW tool:
633# File filename;Line nnnn
634# If it looks like that, then extract the filename and line number
635# and return them in a list, otherwise return an error
636proc SmartOpenListMPW {listItem} \
637{
638	if {[regexp {^File *'?([^' ]+)'? *; *Line *([0-9]+)} $listItem whole tempFile tempLine]} \
639	{
640		if {[file exists $tempFile]} \
641		{
642			lappend temp $tempFile $tempLine;	# make a list of 2 elements (filename and line number)
643			lappend temp2 $temp;				# return list of lists
644			return $temp2;
645		}
646	}
647	return -code error;
648}
649
650# Given an "filename" from a list, try to interpret it as the output from
651# a Visual Studio tool:
652# filename(nnnn)
653# If it looks like that, then extract the filename and line number
654# and return them in a list, otherwise return an error
655proc SmartOpenListVisualStudio {listItem} \
656{
657	if {[regexp {^([^ (]+) *\(([0-9]+)\)} $listItem whole tempFile tempLine]} \
658	{
659		if {[file exists $tempFile]} \
660		{
661			lappend temp $tempFile $tempLine;	# make a list of 2 elements (filename and line number)
662			lappend temp2 $temp;				# return list of lists
663			return $temp2;
664		}
665	}
666	return -code error;
667}
668
669# Given an "filename" from a list, try to interpret it as a 'C' include
670# statement:
671# #include <filename>	(looks in /usr/include)
672# #include "filename"	(looks in current directory)
673# If it looks like that, then extract the filename
674# and return it, otherwise return an error
675proc SmartOpenListInclude {listItem} \
676{
677	if {[regexp "^\[ \t\]*#\[ \t\]*include\[ \t\]+\\<(\[^>\]+)\\>" $listItem whole tempFile]} \
678	{
679		lappend temp "/usr/include/$tempFile"
680		lappend temp2 $temp;				# return list of lists
681		return $temp2;
682	} \
683	else \
684	{
685		if {[regexp "^\[ \t\]*#\[ \t\]*include\[ \t\]+\"(\[^\"\]+)\"" $listItem whole tempFile]} \
686		{
687			lappend temp $tempFile;
688			lappend temp2 $temp;			# return list of lists
689			return $temp2;
690		}
691	}
692	return -code error;
693}
694
695# Given an "filename" from a list, try to interpret it as a globbed list
696# of names.
697# If it looks like that, then extract the filename
698# and return it, otherwise return an error
699proc SmartOpenListGlob {listItem} \
700{
701	if {[catch {glob $listItem} globList]==0} \
702	{
703		return $globList;
704	}
705	return -code error;
706}
707
708# Just like OpenList, but uses an array of functions to try to identify filenames
709# with line numbers that might appear in various forms.
710# If it can find a line number, then it opens the file to that line.
711proc SmartOpenList {list} \
712{
713	set numItems [llength $list]
714	set item 0
715	while {$item<$numItems} \
716	{
717		set listItem [lindex $list $item]
718		incr item
719		set claimed 0
720
721		# strip off newline if it exists
722		regexp "^(\[^\n\]+)" $listItem whole listItem;
723
724		foreach procListElement {SmartOpenListGNU SmartOpenListMPW SmartOpenListVisualStudio SmartOpenListInclude SmartOpenListGlob} \
725		{
726			# see if one of the functions will claim this item as its own
727			if {[catch {set results [$procListElement $listItem]} errorMessage]==0} \
728			{
729				set numResults [llength $results]
730				set resultIndex 0
731				while {$resultIndex<$numResults} \
732				{
733					set result [lindex $results $resultIndex];		# pick out one of the file/line pairs from the return result
734					incr resultIndex;
735					set file [lindex $result 0];					# get the pieces
736					set line [lindex $result 1]
737
738					if {[catch {OpenDefaultWindow [set newBuffer [OpenBufferRecordMtime $file]]} errorMessage]==0} \
739					{
740						if {[string length $line]!=0} \
741						{
742							selectline $newBuffer $line;
743							HomeWindowToSelectionStart $newBuffer;
744						}
745					} \
746					else \
747					{
748						if {$item==$numItems&&$resultIndex==$numResults} \
749						{
750							okdialog "Failed to open '$file'\n$errorMessage"
751						} \
752						else \
753						{
754							okcanceldialog "Failed to open '$file'\n$errorMessage\n\nContinue?"
755						}
756					}
757				}
758				set claimed 1;
759				break;				# had a function claim the list element, so stop searching
760			}
761		}
762		if {$claimed==0} \
763		{
764			if {[catch {OpenDefaultWindow [set newBuffer [OpenBufferRecordMtime $listItem]]} errorMessage]!=0} \
765			{
766				if {$item==$numItems} \
767				{
768					okdialog "Failed to open '$listItem'\n$errorMessage"
769				} \
770				else \
771				{
772					okcanceldialog "Failed to open '$listItem'\n$errorMessage\n\nContinue?"
773				}
774			}
775		}
776	}
777}
778
779# Attempt to include a list of files one at a time, into the given buffer.
780# If any fails to include, report why, and give the user a chance to cancel.
781proc IncludeList {buffer list} \
782{
783	set numItems [llength $list]
784	set item 0
785
786	while {$item<$numItems} \
787	{
788		set file [lindex $list $item]
789		incr item
790		if {[catch {insertfile $buffer $file} errorMessage]!=0} \
791		{
792			if {$item==$numItems} \
793			{
794				okdialog "Failed to include '$file'\n$errorMessage"
795			} \
796			else \
797			{
798				okcanceldialog "Failed to include '$file'\n$errorMessage\n\nContinue?"
799			}
800		}
801	}
802}
803
804# Swap the top two windows on the screen.
805proc SwapWindows {} \
806{
807	set windowList [windowlist]
808	if {[llength $windowList]>1} \
809	{
810		settopwindow [lindex $windowList 1]
811	} \
812	elseif {[llength $windowList]>0} \
813	{
814		settopwindow [lindex $windowList 0]
815	} \
816	else \
817	{
818		beep
819	}
820}
821
822# Bring the bottom window to the top.
823proc RotateWindows {} \
824{
825	set windowList [windowlist]
826	set numWindows [llength $windowList]
827	if {$numWindows>1} \
828	{
829		incr numWindows -1
830		settopwindow [lindex $windowList $numWindows]
831	} \
832	elseif {$numWindows>0} \
833	{
834		settopwindow [lindex $windowList 0]
835	} \
836	else \
837	{
838		beep
839	}
840}
841
842# Choose a window to be top-most (this places a * next to windows which have
843# been modified)
844proc ChooseWindow {} \
845{
846	set newList "";
847	foreach window [windowlist]\
848	{
849		if {[isdirty $window]} \
850		{
851			lappend newList "* $window";
852		} \
853		else \
854		{
855			lappend newList "  $window";
856		}
857 	}
858	foreach window [listdialog "Choose a Window:" $newList] \
859	{
860		settopwindow [string range $window 2 end];	# select the window (removing the modified indication)
861	}
862}
863
864# Return the active window if there is one, otherwise
865# just beep, and return an error.
866proc ActiveWindowOrBeep {} \
867{
868	if {[catch {activewindow} message]==0} \
869	{
870		return $message
871	}
872	beep
873	return -code error
874}
875
876# Return the current clipboard if there is one, otherwise
877# just beep, and return an error.
878proc CurrentClipboardOrBeep {} \
879{
880	if {[catch {getclipboard} message]==0} \
881	{
882		 return $message
883	}
884	beep
885	return -code error
886}
887
888# Report the current clipboard, or a message that indicates that
889# there is none
890proc ShowCurrentClipboard {} \
891{
892	if {[catch {getclipboard} message]==0} \
893	{
894		okdialog "Current clipboard:\n\n$message"
895	} \
896	else \
897	{
898		okdialog "There is no current clipboard"
899	}
900}
901
902# Quiz user to find out if he really wants to "revert".
903proc AskRevert {buffer} \
904{
905	if {[isdirty $buffer]} \
906	{
907		okcanceldialog "Do you really want to discard changes to:\n'$buffer'"
908		revertbuffer $buffer
909		if {[fromfile $buffer]} \
910		{
911			catch {setbuffervariable $buffer mtime [file mtime $buffer]};		# update the modification time so we can look at it when saving
912		}
913	} \
914	else \
915	{
916		revertbuffer $buffer;			# may be reverting because the file was changed, so just do it without asking
917		if {[fromfile $buffer]} \
918		{
919			catch {setbuffervariable $buffer mtime [file mtime $buffer]};		# update the modification time so we can look at it when saving
920		}
921	}
922}
923
924# Get name of file to "save as", then do it for the given buffer.
925# NOTE: this returns the new name of the buffer if it completes successfully.
926proc AskSaveAs {buffer} \
927{
928	set newPath [savedialog "Save File:" $buffer]
929	if {[file exists $newPath]} \
930	{
931		okcanceldialog "File:\n'$newPath'\nalready exists. Overwrite?"
932	}
933	if {[catch {savebufferas $buffer $newPath} message]==0} \
934	{
935		catch {setbuffervariable $message mtime [file mtime $message]};		# update the modification time so we can look at it when saving
936	} \
937	else \
938	{
939		okdialog "Failed to save:\n'$newPath'\n$message"
940		return -code error
941	}
942	UpdateStyle $message;				# update the styles of this window
943	return $message;					# return the new name
944}
945
946# Get name of file to "save to", then do it for the given buffer.
947proc AskSaveTo {buffer} \
948{
949	set newPath [savedialog "Save Copy To:" $buffer]
950	if {[file exists $newPath]} \
951	{
952		okcanceldialog "File:\n'$newPath'\nalready exists. Overwrite?"
953	}
954	if {[catch {savebufferto $buffer $newPath} errorMessage]!=0} \
955	{
956		okdialog "Failed to save:\n'$newPath'\n$errorMessage"
957		return -code error
958	}
959}
960
961# See if buffer is not linked to a file, and if not, ask for
962# a file name to save it to, else just save it.
963# Either way, return the name of the buffer after it is saved,
964# as it may have changed during the save process.
965proc AskSave {buffer} \
966{
967	if {[fromfile $buffer]} \
968	{
969		# see if there is a modification time tag associated with this file, if so, check against current mtime and complain if they differ
970		if {[catch {getbuffervariable $buffer mtime} oldmtime]==0} \
971		{
972			if {[catch {file mtime $buffer} newmtime]==0} \
973			{
974				if {$oldmtime!=$newmtime} \
975				{
976					okcanceldialog "WARNING!\n\nIt looks like another application modified:\n$buffer\nwhile it was open for editing.\n\nWould you like to save anyway?"
977				}
978			} \
979			else \
980			{
981				okcanceldialog "WARNING!\n\nIt looks like another application deleted:\n$buffer\nwhile it was open for editing.\n\nWould you like to save anyway?"
982			}
983		}
984
985		if {[catch {savebuffer $buffer} message]==0} \
986		{
987			catch {setbuffervariable $buffer mtime [file mtime $buffer]};		# update the modification time so we can look at it next time we save
988		} \
989		else \
990		{
991			okdialog "Failed to save:\n'$buffer'\n$message"
992			return -code error
993		}
994	} \
995	else \
996	{
997		set buffer [AskSaveAs $buffer];				# pick up the new name of the buffer
998	}
999	return $buffer
1000}
1001
1002# Ask the user if he really wants to save all the dirty windows, if so, do it.
1003proc AskSaveAll {} \
1004{
1005	ActiveWindowOrBeep;										# make sure there is a window, if not, just beep and leave
1006	okcanceldialog "Really save all modified windows?"
1007	foreach window [windowlist] \
1008	{
1009		if {[isdirty $window]} \
1010		{
1011			if {[catch {AskSave $window}]!=0} \
1012			{
1013				okdialog "Save All aborted\n"
1014				return -code error
1015			}
1016		}
1017	}
1018}
1019
1020# See if buffer is dirty before closing.
1021# If it is, give user a chance to save.
1022proc AskClose {buffer} \
1023{
1024	# See if buffer points to a something low-priority, we just close the windows on them, not the buffer
1025	if {[LowPriority $buffer]} \
1026	{
1027		closewindow $buffer
1028	} \
1029	else \
1030	{
1031		if {[isdirty $buffer]} \
1032		{
1033			settopwindow $buffer;							# pop it to the top of the window stack
1034
1035			if {[yesnodialog "Save Changes To:\n'$buffer'\nBefore Closing?"]} \
1036			{
1037				set buffer [AskSave $buffer];				# if it is saved, get the new name so we can close it
1038			}
1039		}
1040		closebuffer $buffer
1041	}
1042}
1043
1044# Ask for a list of windows which should be printed
1045proc PrintWindows {} \
1046{
1047	global defaultPrintCommand;
1048
1049	foreach buffer [listdialog "Choose Window(s) to Print:" [windowlist]] \
1050	{
1051		set command [textdialog "If needed, modify the print command below:" "[eval format "%s" \"$defaultPrintCommand\"]"]
1052		setmark $buffer temp;						# remember what was selected, we will not disturb it
1053		selectall $buffer;							# make a selection of all of the text in the passed buffer
1054		if {[catch {eval exec $command << {[lindex [selectedtextlist $buffer] 0]}} result]!=0} \
1055		{
1056			okdialog "Print status:\n\n$result\n"
1057		}
1058		gotomark $buffer temp;						# put selection back
1059		closemark $buffer temp;						# get rid of temp selection
1060	}
1061}
1062
1063# Attempt to get lpr to output this document to the local printer
1064proc PrintBuffer {buffer} \
1065{
1066	global defaultPrintCommand;
1067
1068	set command [textdialog "If needed, modify the print command below:" "[eval format "%s" \"$defaultPrintCommand\"]"]
1069	setmark $buffer temp;							# remember what was selected, we will not disturb it
1070	selectall $buffer;								# make a selection of all of the text in the passed buffer
1071	if {[catch {eval exec $command << {[lindex [selectedtextlist $buffer] 0]}} result]!=0} \
1072	{
1073		okdialog "Print status:\n\n$result\n"
1074	}
1075	gotomark $buffer temp;							# put selection back
1076	closemark $buffer temp;							# get rid of temp selection
1077}
1078
1079# print the first selection to the local printer
1080proc PrintSelection {buffer} \
1081{
1082	global defaultPrintCommand;
1083
1084	if {[lindex [selectioninfo $buffer] 6]} \
1085	{
1086		set command [textdialog "If needed, modify the print command below:" "[eval format "%s" \"$defaultPrintCommand\"]"]
1087		if {[catch {eval exec $command << {[lindex [selectedtextlist $buffer] 0]}} result]!=0} \
1088		{
1089			okdialog "Print status:\n\n$result\n"
1090		}
1091	} \
1092	else \
1093	{
1094		beep
1095	}
1096}
1097
1098# Attempt to close all open windows, asking to save any that are dirty.
1099# If the user cancels, then back out, otherwise, quit.
1100proc TryToQuit {} \
1101{
1102	foreach window [windowlist] \
1103	{
1104		AskClose $window
1105	}
1106	forceQUIT
1107}
1108
1109# Just like find, but will place the result in a dialog if it fails, and home if it succeeds, or beep if it finds nothing.
1110proc FindBeep {window buffer args} \
1111{
1112	if {[catch {eval [list find $window $buffer] $args} message]==0} \
1113	{
1114		if {[llength $message]} {homewindow $window [lindex $message 0] [lindex $message 1]} else {beep}
1115	} \
1116	else \
1117	{
1118		okdialog $message
1119	}
1120}
1121
1122# Just like findall, but will place the result in a dialog if it fails, and home if it succeeds, or beep if it finds nothing.
1123proc FindAllBeep {window buffer args} \
1124{
1125	if {[catch {eval [list findall $window $buffer] $args} message]==0} \
1126	{
1127		if {[llength $message]} {homewindow $window [lindex $message 0] [lindex $message 1]} else {beep}
1128	} \
1129	else \
1130	{
1131		okdialog $message
1132	}
1133}
1134
1135# Just like replace, but will place the result in a dialog if it fails, and home if it succeeds, or beep if it finds nothing.
1136proc ReplaceBeep {window findBuffer replaceBuffer args} \
1137{
1138	if {[catch {eval [list replace $window $findBuffer $replaceBuffer] $args} message]==0} \
1139	{
1140		if {[llength $message]} {homewindow $window [lindex $message 0] [lindex $message 1]} else {beep}
1141	} \
1142	else \
1143	{
1144		okdialog $message
1145	}
1146}
1147
1148# Just like replaceall, but will place the result in a dialog if it fails, and home if it succeeds, or beep if it finds nothing.
1149proc ReplaceAllBeep {window findBuffer replaceBuffer args} \
1150{
1151	if {[catch {eval [list replaceall $window $findBuffer $replaceBuffer] $args} message]==0} \
1152	{
1153		if {[llength $message]} {homewindow $window [lindex $message 0] [lindex $message 1]} else {beep}
1154	} \
1155	else \
1156	{
1157		okdialog $message
1158	}
1159}
1160
1161# Turn the booleans that come back from the search dialog into flags which can be passed
1162# to the search functions
1163proc CreateSearchOptions {} \
1164{
1165	global sd_backwards sd_wrapAround sd_selectionExpression sd_ignoreCase sd_limitScope sd_replaceProc
1166
1167	set result ""
1168	if {$sd_backwards!=0} \
1169	{
1170		lappend result "-backward";
1171	}
1172	if {$sd_wrapAround!=0} \
1173	{
1174		lappend result "-wrap";
1175	}
1176	if {$sd_selectionExpression!=0} \
1177	{
1178		lappend result "-regex";
1179	}
1180	if {$sd_ignoreCase!=0} \
1181	{
1182		lappend result "-ignorecase";
1183	}
1184	if {$sd_limitScope!=0} \
1185	{
1186		lappend result "-limitscope";
1187	}
1188	if {$sd_replaceProc!=0} \
1189	{
1190		lappend result "-replacescript";
1191	}
1192	return $result
1193}
1194
1195# Ask the user just what he wishes to search for, and how, then do it in all open windows if he does not cancel.
1196proc AskSearchAll {} \
1197{
1198	global sd_backwards sd_wrapAround sd_selectionExpression sd_ignoreCase sd_limitScope sd_replaceProc
1199
1200	set searchType [searchdialog "" findBuffer replaceBuffer sd_backwards sd_wrapAround sd_selectionExpression sd_ignoreCase sd_limitScope sd_replaceProc]
1201	foreach window [windowlist] \
1202	{
1203		switch $searchType \
1204		{
1205			find
1206			{
1207				eval [list FindBeep $window findBuffer] [CreateSearchOptions]
1208			}
1209			findall
1210			{
1211				eval [list FindAllBeep $window findBuffer] [CreateSearchOptions]
1212			}
1213			replace
1214			{
1215				eval [list ReplaceBeep $window findBuffer replaceBuffer] [CreateSearchOptions]
1216			}
1217			replaceall
1218			{
1219				eval [list ReplaceAllBeep $window findBuffer replaceBuffer] [CreateSearchOptions]
1220			}
1221		}
1222	}
1223	okdialog "All windows completed"
1224}
1225
1226# Ask the user just what he wishes to search for, and how, then do it if he does not cancel.
1227proc AskSearch {window} \
1228{
1229	global sd_backwards sd_wrapAround sd_selectionExpression sd_ignoreCase sd_limitScope sd_replaceProc
1230
1231	set searchType [searchdialog "" findBuffer replaceBuffer sd_backwards sd_wrapAround sd_selectionExpression sd_ignoreCase sd_limitScope sd_replaceProc]
1232	switch $searchType \
1233	{
1234		find
1235		{
1236			eval [list FindBeep $window findBuffer] [CreateSearchOptions]
1237		}
1238		findall
1239		{
1240			eval [list FindAllBeep $window findBuffer] [CreateSearchOptions]
1241		}
1242		replace
1243		{
1244			eval [list ReplaceBeep $window findBuffer replaceBuffer] [CreateSearchOptions]
1245		}
1246		replaceall
1247		{
1248			eval [list ReplaceAllBeep $window findBuffer replaceBuffer] [CreateSearchOptions]
1249		}
1250	}
1251}
1252
1253# Used for find same backwards/forwards.
1254proc FindNext {window backward} \
1255{
1256	set flags [CreateSearchOptions]
1257	# if asked to search backwards, then add this flag to the list
1258	if {$backward!=0} \
1259	{
1260		lappend flags "-backward"
1261	} \
1262	else \
1263	{
1264		lappend flags "-forward"
1265	}
1266	eval [list FindBeep $window findBuffer] $flags
1267}
1268
1269# if selection then first copy the selection and then do FindAllBeep
1270# if no selection then try to find what is in the findBuffer
1271proc FindSelectionNext {window backward} \
1272{
1273	set ends [getselectionends $window]
1274	set start [lindex $ends 0]
1275	set end [lindex $ends 1]
1276	if {$start!=$end} \
1277	{
1278		copy $window findBuffer
1279	}
1280	FindNext $window $backward
1281}
1282
1283# Used for replace same backwards/forwards.
1284proc ReplaceNext {window backward} \
1285{
1286	set flags [CreateSearchOptions]
1287	# if asked to search backwards, then add this flag to the list
1288	if {$backward!=0} \
1289	{
1290		lappend flags "-backward"
1291	} \
1292	else \
1293	{
1294		lappend flags "-forward"
1295	}
1296	eval [list ReplaceBeep $window findBuffer replaceBuffer] $flags
1297}
1298
1299# Replace all selections with some given text.
1300proc ReplaceSelections {window} \
1301{
1302	global replaceSelectionsChoice;
1303
1304	TextToBuffer tempFindBuffer {([^]+)};							# load up the expression (find anything, mark as \0)
1305	TextToBuffer tempReplaceBuffer [set replaceSelectionsChoice [textdialog "Replacement string?" $replaceSelectionsChoice]];	# load up the replacement
1306	ReplaceAllBeep $window tempFindBuffer tempReplaceBuffer -regex -limitscope;	# do the replacement
1307}
1308
1309# Shift the text but attempt to leave the selection as little disturbed as possible
1310proc HandleShifts {buffer} \
1311{
1312	set numSelections [lindex [selectioninfo $buffer] 6]
1313
1314	if {$numSelections>0} \
1315	{
1316		# if one selection, try not to "columnarize" it
1317		if {$numSelections==1} \
1318		{
1319			catch {closebuffer tempBuffer};						# get rid of any lingering tempBuffer
1320			newbuffer tempBuffer;								# create a place to do some messing around in
1321			copy $buffer tempBuffer;							# move text in question to temp buffer
1322			setselectionends tempBuffer 0 0;					# move to top for replace
1323
1324			set start [lindex [getselectionends $buffer] 0];	# get start of selection
1325
1326			if {[catch {replaceall tempBuffer tempFindBuffer tempReplaceBuffer -regex} message]==0} \
1327			{
1328				if {[llength $message]} \
1329				{
1330					setselectionends tempBuffer 0 0;			# get rid of selections in temp buffer so paste does what we want
1331					paste $buffer tempBuffer;					# place results back, write over old selection
1332					set length [lindex [textinfo tempBuffer] 1]
1333					setselectionends $buffer $start [expr $start+$length] ;	# re-select the block
1334				} \
1335				else \
1336				{
1337					beep
1338				}
1339			} \
1340			else \
1341			{
1342				okdialog $message
1343			}
1344			closebuffer tempBuffer;
1345		} \
1346		else \
1347		{
1348			if {[catch {replaceall $buffer tempFindBuffer tempReplaceBuffer -regex -limitscope} message]==0} \
1349			{
1350				if {[llength $message]==0} {beep}
1351			} \
1352			else \
1353			{
1354				okdialog $message
1355			}
1356		}
1357	} \
1358	else \
1359	{
1360		beep
1361	}
1362}
1363
1364# Align text left, removing all tabs or spaces at the start of lines in the selection.
1365proc AlignLeft {buffer} \
1366{
1367	TextToBuffer tempFindBuffer {^[\t ]+(.*)|(.+)};				# load up the expression
1368	TextToBuffer tempReplaceBuffer {\0\1};						# load up the replacement
1369	HandleShifts $buffer;
1370}
1371
1372# Shift text left, removing tabs or spaces at the start of lines in the selection.
1373proc ShiftLeft {buffer} \
1374{
1375	TextToBuffer tempFindBuffer {^[\t ](.*)|(.+)};				# load up the expression (expression is slightly strange, so that lines which are not altered are not deselected)
1376	TextToBuffer tempReplaceBuffer {\0\1};						# load up the replacement
1377	HandleShifts $buffer;
1378}
1379
1380# Shift text right, adding tabs at the start of lines in the selection.
1381proc ShiftRight {buffer} \
1382{
1383	TextToBuffer tempFindBuffer {^(.+)};						# load up the expression
1384	TextToBuffer tempReplaceBuffer {	\0};					# load up the replacement
1385	HandleShifts $buffer;
1386}
1387
1388# Unselect any whitspace which is currently selected.
1389# This is sometimes useful during columnar select when tabs or spaces
1390# get in the way
1391proc UnselectWhitespace {buffer} \
1392{
1393	TextToBuffer tempFindBuffer {[^ \t\n]+};					# load up the expression to match any non-white characters
1394	if {[catch {findall $buffer tempFindBuffer -regex -limitscope} message]==0} \
1395	{
1396		if {[llength $message]==0} \
1397		{
1398			# no non-white characters were located, so selection is ALL white, just eliminate the selection
1399			set start [lindex [getselectionends $buffer] 0];	# get start of selection
1400			setselectionends $buffer $start $start;				# reduce selection to nothing
1401		}
1402	} \
1403	else \
1404	{
1405		okdialog $message
1406	}
1407}
1408
1409# Report interesting information about the passed buffer
1410proc BufferInfo {buffer} \
1411{
1412	set textInfo [textinfo $buffer]
1413	set textLines [lindex $textInfo 0]
1414	set textChars [lindex $textInfo 1]
1415
1416	set selectionInfo [selectioninfo $buffer]
1417	set startPosition [lindex $selectionInfo 0]
1418	set endPosition [lindex $selectionInfo 1]
1419	set startLine [lindex $selectionInfo 2]
1420	set endLine [lindex $selectionInfo 3]
1421	set startLinePosition [lindex $selectionInfo 4]
1422	set endLinePosition [lindex $selectionInfo 5]
1423	set totalSegments [lindex $selectionInfo 6]
1424	set totalSpan [lindex $selectionInfo 7]
1425
1426	if {$endLinePosition&&$totalSpan} \
1427	{
1428		set totalLines [expr $endLine-$startLine+1];	# this is kind of screwy, but it gives the results that are easiest for the user to understand
1429	} \
1430	else \
1431	{
1432		set totalLines [expr $endLine-$startLine]
1433	}
1434
1435	okdialog "\
1436$buffer\n\n\
1437Tab stops                [gettabsize $buffer]\n\
1438Total lines              $textLines\n\
1439Total bytes              $textChars\n\
1440Selection start position $startPosition ($startLine:$startLinePosition)\n\
1441Selection end position   $endPosition ($endLine:$endLinePosition)\n\
1442Total selection segments $totalSegments\n\
1443Total selected chars     $totalSpan\n\
1444Total lines spanned      $totalLines\n"
1445}
1446
1447# Pipe the selections through a unix command, collect output, and replace selections.
1448proc PipeSelection {window} \
1449{
1450	global pipeSelectionsChoice;
1451
1452	set command [set pipeSelectionsChoice [textdialog "Enter command to pipe selections through:" $pipeSelectionsChoice]]
1453	TextToBuffer tempFindBuffer {[^]+};							# load up the expression (find absolutely anything)
1454	TextToBuffer tempReplaceBuffer "catch \{exec -keepnewline $command <<\$found\} message; set message";	# load up the replacement, return results of command
1455	ReplaceAllBeep $window tempFindBuffer tempReplaceBuffer -regex -limitscope -replacescript;			# do the replacement
1456}
1457
1458# Convert all letters of the given selection to upper case.
1459proc UppercaseSelection window \
1460{
1461	TextToBuffer tempFindBuffer {[^]+};							# load up the expression (find anything)
1462	TextToBuffer tempReplaceBuffer "string toupper \$found";	# load up the replacement
1463	ReplaceAllBeep $window tempFindBuffer tempReplaceBuffer -regex -limitscope -replacescript;	# do the replacement
1464}
1465
1466# Convert all letters of the given selection to lower case.
1467proc LowercaseSelection window \
1468{
1469	TextToBuffer tempFindBuffer {[^]+};							# load up the expression (find anything)
1470	TextToBuffer tempReplaceBuffer "string tolower \$found";	# load up the replacement
1471	ReplaceAllBeep $window tempFindBuffer tempReplaceBuffer -regex -limitscope -replacescript;	# do the replacement
1472}
1473
1474# Locate all the numbers in the current selection, and increment them by the given amount.
1475proc IncrementSelection {window amount} \
1476{
1477	TextToBuffer tempFindBuffer {-?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))};		# load up the expression (find any (possibly floating point) number including negative ones)
1478	TextToBuffer tempReplaceBuffer "expr [string trimleft \$found 0] + $amount";	# load up the replacement, return the result of adding amount
1479	ReplaceAllBeep $window tempFindBuffer tempReplaceBuffer -regex -limitscope -replacescript;	# do the replacement
1480}
1481
1482# Replace each selection by an incrementing number.
1483proc EnumerateSelection {window startAt amount} \
1484{
1485	global enumStart
1486	set enumStart $startAt
1487	TextToBuffer tempFindBuffer {.+};							# load up the expression (find anything)
1488	TextToBuffer tempReplaceBuffer "global enumStart;set temp \$enumStart;incr enumStart $amount;format %d \$temp";		# load up the replacement, return an incremented number
1489	ReplaceAllBeep $window tempFindBuffer tempReplaceBuffer -regex -limitscope -replacescript;	# do the replacement
1490}
1491
1492# Report the sum of all selected numbers.
1493proc SumSelection {window} \
1494{
1495	setmark $window temp;										# remember what was selected, we will not disturb it
1496	TextToBuffer tempFindBuffer {-?(([0-9]+(\.[0-9]+)?)|(\.[0-9]+))};		# load up the expression (find any (possibly floating point) number including negative ones)
1497	if {[catch {findall $window tempFindBuffer -regex -limitscope} message]==0} \
1498	{
1499		if {[llength $message]} \
1500		{
1501			set total 0
1502			foreach number [selectedtextlist $window] \
1503			{
1504				set total [expr [string trimleft $number 0]+$total];	# convert number with leading 0's to DECIMAL, not octal!
1505			}
1506			okdialog "Sum = $total"
1507		} \
1508		else \
1509		{
1510			beep
1511		}
1512	} \
1513	else \
1514	{
1515		okdialog $message
1516	}
1517	gotomark $window temp;									# put back the user's selection
1518	closemark $window temp;									# get rid of temp selection
1519}
1520
1521# Sort the selections, and replace them in sorted order.
1522proc SortSelection {window} \
1523{
1524	global sortedArray sortedIndex
1525	catch {unset sortedArray}
1526	set sortedIndex 0
1527	foreach element [lsort [selectedtextlist $window]] \
1528	{
1529		set sortedArray($sortedIndex) $element;				# copy elements into an array to speed things up
1530		incr sortedIndex
1531	}
1532	set sortedIndex 0
1533	TextToBuffer tempFindBuffer {[^]+};							# load up the expression (find absolutely anything)
1534	TextToBuffer tempReplaceBuffer {global sortedArray sortedIndex;incr sortedIndex;set sortedArray([expr $sortedIndex-1])};		# load up the replacement, return an entry from the array
1535	ReplaceAllBeep $window tempFindBuffer tempReplaceBuffer -regex -limitscope -replacescript;	# do the replacement
1536	catch {unset sortedArray}
1537	unset sortedIndex
1538}
1539
1540# Replace the selections in reverse order.
1541proc ReverseSelection {window} \
1542{
1543	global reverseArray reverseIndex
1544	catch {unset reverseArray}
1545	set reverseIndex 0
1546	foreach element [selectedtextlist $window] \
1547	{
1548		set reverseArray($reverseIndex) $element;			# copy elements into an array to speed things up
1549		incr reverseIndex
1550	}
1551	TextToBuffer tempFindBuffer {[^]+};							# load up the expression (find absolutely anything)
1552	TextToBuffer tempReplaceBuffer {global reverseArray reverseIndex;incr reverseIndex -1;set reverseArray($reverseIndex)};	# load up the replacement, return an entry from the array
1553	ReplaceAllBeep $window tempFindBuffer tempReplaceBuffer -regex -limitscope -replacescript;	# do the replacement
1554	catch {unset reverseArray}
1555	unset reverseIndex
1556}
1557
1558# Get a manual page, and dump it into a window.
1559proc ManPage params \
1560{
1561	global staggerOpenX staggerOpenY windowHeight style_keyword style_string
1562
1563	set name [newbuffer [NewWindowName]];			# make a buffer to hold the man page
1564	# move stdin to /dev/null so that man won't look at current terminal width
1565	execute $name "catch \{exec man -Tascii $params </dev/null\} result; set result";
1566
1567# man thinks it is outputing to a printer where it can backspace over stuff to get effects
1568# like BOLD, or dot points. The replace strips out the mess
1569
1570# first, go through and make keywords out of anything man was trying to make bold
1571	setselectionends $name 0 0;						# move to the top of the man page buffer
1572	TextToBuffer tempFindBuffer {((.)\x08\1)+};		# load up the expression
1573	catch {findall $name tempFindBuffer -regex};	# do this, ignore any errors
1574	selectiontostyle $name $style_keyword;			# set what we found to style_keyword
1575
1576# now, go through and make strings out of anything man was trying to make underlines
1577	setselectionends $name 0 0;						# move to the top of the man page buffer
1578	TextToBuffer tempFindBuffer {(_\x08[^\n_])+};	# load up the expression
1579	catch {findall $name tempFindBuffer -regex};	# do this, ignore any errors
1580	selectiontostyle $name $style_string;			# set what we found to style_string
1581
1582# remove all the backspace characters
1583	setselectionends $name 0 0;						# move to the top of the man page buffer
1584	TextToBuffer tempFindBuffer {.\x08};			# load up the expression
1585	TextToBuffer tempReplaceBuffer {};				# replace with nothing
1586	catch {replaceall $name tempFindBuffer tempReplaceBuffer -regex};	# do this, ignore any errors
1587
1588# replace special double-quote chars
1589	setselectionends $name 0 0;										 	# move to the top of the man page buffer
1590	TextToBuffer tempFindBuffer {(\xE2\x80[\x98,\x99]){2}};				# load up the expression
1591	TextToBuffer tempReplaceBuffer {"};									# replace with nothing
1592	catch {replaceall $name tempFindBuffer tempReplaceBuffer -regex};	# do this, ignore any errors
1593
1594# replace special single-quote chars
1595	setselectionends $name 0 0;											# move to the top of the man page buffer
1596	TextToBuffer tempFindBuffer {\xE2\x80[\x98,\x99]};					# load up the expression
1597	TextToBuffer tempReplaceBuffer {'};									# replace with nothing
1598	catch {replaceall $name tempFindBuffer tempReplaceBuffer -regex};	# do this, ignore any errors
1599
1600# get rid of any huge page gaps
1601	setselectionends $name 0 0;						# move to the top of the buffer again
1602	TextToBuffer tempFindBuffer {^\n{2,}};			# load up the expression
1603	TextToBuffer tempReplaceBuffer "\n";			# replace with nothing
1604	catch {replaceall $name tempFindBuffer tempReplaceBuffer -regex};	# do this, ignore any errors
1605	setselectionends $name 0 0;						# move to the top of the buffer again
1606
1607	flushundos $name;								# get rid of any undos we made here, just to be nice
1608	cleardirty $name;								# make it non-modified
1609
1610	OpenWindow $name $staggerOpenX $staggerOpenY [expr 8*80] $windowHeight 8 "Man Page" ""
1611	UpdateStaggerPosition;
1612}
1613
1614# Refill a selected paragraph(s), so that the lines of text do not extend past the given
1615# column. This is a little ugly, but it does the job quite nicely. This also works if the
1616# selection is columnar (can be used to refill comments)
1617proc RefillSelection buffer \
1618{
1619	global refillLimitChoice;
1620
1621	set limit [expr [set refillLimitChoice [textdialog "Max length:" $refillLimitChoice]]];		# get length limit from user
1622
1623	set ends [getselectionends $buffer]
1624	set start [lindex $ends 0]
1625	set end [lindex $ends 1]
1626	if {$start!=$end} \
1627	{
1628		catch {closebuffer tempBuffer};					# get rid of any lingering tempBuffer
1629		newbuffer tempBuffer;							# create a place to do some messing around in
1630		copy $buffer tempBuffer;						# move text in question to temp buffer
1631
1632		TextToBuffer tempFindBuffer "(^\[ \\t\]+)|(\[ \t\]+$)";		# remove white space at starts and ends of lines
1633		TextToBuffer tempReplaceBuffer {};
1634		setselectionends tempBuffer 0 0;				# move to top for replace
1635		catch {replaceall tempBuffer tempFindBuffer tempReplaceBuffer -regex};	# do this, ignore any errors
1636
1637		TextToBuffer tempFindBuffer "\[ \\t\]+";		# make tabs, or multiple spaces into single spaces
1638		TextToBuffer tempReplaceBuffer { };
1639		setselectionends tempBuffer 0 0;				# move to top for replace
1640		catch {replaceall tempBuffer tempFindBuffer tempReplaceBuffer -regex};	# do this, ignore any errors
1641
1642		TextToBuffer tempFindBuffer "(^\\n)|(\\n)";		# make all newlines at the ends of lines into spaces
1643		TextToBuffer tempReplaceBuffer {if {[string length $1] > 0} {set found { }}; set found};
1644		selectall tempBuffer;							# select all so replace will start at the bottom (replacing backwards)
1645		catch {replaceall tempBuffer tempFindBuffer tempReplaceBuffer -backward -regex -replacescript};	# do this, ignore any errors
1646
1647		TextToBuffer tempFindBuffer "(.\{1,$limit\})((\[ \\t\]+)|(.$))";	# cut lines to length at whitespace
1648		TextToBuffer tempReplaceBuffer {if {[string length $2] > 0} {format "%s\n" $0} else {format "%s%s" $0 $3}};
1649		setselectionends tempBuffer 0 0;				# move to top for replace
1650		catch {replaceall tempBuffer tempFindBuffer tempReplaceBuffer -regex -replacescript};	# do this, ignore any errors
1651
1652		setselectionends tempBuffer 0 0;				# move to top of temp buffer
1653
1654		set startNumSelections [lindex [selectioninfo $buffer] 6];
1655		if {$startNumSelections>1} \
1656		{
1657			TextToBuffer tempFindBuffer "^.*$";			# select all the lines individually
1658			catch {findall tempBuffer tempFindBuffer -regex};	# do this, ignore any errors
1659
1660			set endNumSelections [lindex [selectioninfo tempBuffer] 6];
1661			if {$endNumSelections>$startNumSelections} \
1662			{
1663				addselectionendlist tempBuffer [list [list [lineoffsettoposition tempBuffer $startNumSelections 0] [lineoffsettoposition tempBuffer [expr $endNumSelections+1] 0]]]
1664			}
1665		}
1666
1667		paste $buffer tempBuffer;					# place results back, write over old selection(s)
1668
1669		closebuffer tempBuffer;
1670	} \
1671	else \
1672	{
1673		beep;
1674	}
1675}
1676
1677# Check spelling in buffer, make a new window containing the misspelled words.
1678proc SpellDocument buffer \
1679{
1680	global staggerOpenX staggerOpenY windowHeight
1681
1682	set name [newbuffer [NewWindowName]];			# make a buffer to hold the output of the spell command
1683	setmark $buffer temp;							# remember what was selected, we will not disturb it
1684	selectall $buffer;								# make a selection of all of the text in the passed buffer
1685	execute $name {catch {exec aspell list << [lindex [selectedtextlist $buffer] 0] | sort -u} result; set result};
1686	gotomark $buffer temp;							# put selection back
1687	setselectionends $name 0 0;						# move to the top of the output buffer
1688
1689	flushundos $name;								# get rid of any undos we made here, just to be nice
1690	cleardirty $name;								# make it non-modified
1691	openwindow $name $staggerOpenX $staggerOpenY [expr 7*50] $windowHeight "" 8 black lightyellow;	# make a new window, set width to roughly 50 columns of an 7 pixel wide font
1692	UpdateStaggerPosition;
1693	closemark $buffer temp;							# get rid of temp selection
1694}
1695
1696# Get an address, and mail the contents of buffer
1697# to that address.
1698# NOTE: this would be much better handled with a Tk dialog
1699proc MailDocument buffer \
1700{
1701	global mailRecipientChoice;
1702	global mailSubjectChoice;
1703
1704	# get recipient
1705	set recipient [set mailRecipientChoice [textdialog "Mail $buffer To:" $mailRecipientChoice]]
1706	# get subject
1707	set subject [set mailSubjectChoice [textdialog "Subject:" $mailSubjectChoice]]
1708
1709	catch {closebuffer tempBuffer};					# get rid of any lingering tempBuffer
1710	newbuffer tempBuffer;							# create temp buffer
1711	setmark $buffer temp;							# remember what was selected, we will not disturb it
1712	selectall $buffer;								# make a selection of all of the text in the passed buffer
1713	copy $buffer tempBuffer;						# copy selection to temp buffer
1714	gotomark $buffer temp;							# put selection back
1715	closemark $buffer temp;							# get rid of temp selection
1716	setselectionends tempBuffer 0 0;				# move to the top of the temp buffer
1717	insert tempBuffer "To: $recipient\nSubject: $subject\n\n";	# insert recipient and subject to buffer
1718	set result 0;									# predefine no error happend
1719	selectall tempBuffer;							# select what to mail
1720
1721	if {[catch {exec /usr/lib/sendmail -t << [lindex [selectedtextlist tempBuffer] 0]} result]!=0} \
1722	{
1723		okdialog "Mail status:\n\n$result\n"
1724	}
1725	closebuffer tempBuffer;								# get rid of temp buffer
1726}
1727
1728# mail contents of chosen windows to chosen recipients
1729proc MailWindows {} \
1730{
1731	foreach buffer [listdialog "Choose Window(s) to Mail:" [windowlist]] \
1732	{
1733		MailDocument $buffer
1734	}
1735}
1736
1737# Strip the white space at the ends of lines, attempt to leave the selection as it was.
1738# return the number of characters removed
1739proc StripWhite {window} \
1740{
1741	set initialChars [lindex [textinfo $window] 1];			# find out how many characters we have at the start
1742	set numRemoved 0;
1743	setmark $window temp;									# remember what was selected, we will not disturb it
1744	TextToBuffer tempFindBuffer {[ \t]+$};					# load up the expression
1745	TextToBuffer tempReplaceBuffer {};						# replace with nothing
1746	setselectionends $window 0 0;							# move to the top of the buffer to perform the search
1747	if {[catch {replaceall $window tempFindBuffer tempReplaceBuffer -regex} message]==0} \
1748	{
1749		set finalChars [lindex [textinfo $window] 1];		# find out how many characters we have now that we are done
1750		set numRemoved [expr $initialChars-$finalChars];
1751	} \
1752	else \
1753	{
1754		okdialog $message
1755	}
1756	gotomark $window temp;									# put back the user's selection
1757	closemark $window temp;									# get rid of temp selection
1758	return $numRemoved
1759}
1760
1761# Set line termination to the given sequence
1762proc SetLineTermination {window termination} \
1763{
1764	setmark $window temp;									# remember what was selected, we will not disturb it
1765	TextToBuffer tempFindBuffer {\r\n|\r|\n};				# load up the expression
1766	TextToBuffer tempReplaceBuffer $termination;			# replace with given characters
1767	setselectionends $window 0 0;							# move to the top of the buffer to perform the search
1768	catch {replaceall $window tempFindBuffer tempReplaceBuffer -regex}
1769	gotomark $window temp;									# put back the user's selection
1770	closemark $window temp;									# get rid of temp selection
1771}
1772
1773# insert a character into window, and then home the window so that
1774# the character is visible
1775proc InsertAndHome {window character} \
1776{
1777	insert $window $character;								# insert the character
1778	HomeWindowToSelectionStart $window -lenient;			# make sure it is visible
1779}
1780
1781# Inserts newline if the cursor position is above a given value
1782# This is a simple way to implement wrapping lines at word boundaries
1783# Thanks to Juergen Reiss
1784proc WrapLine buffer \
1785{
1786	global lineWrap;
1787	global lineWrapColumn;
1788	set ends [getselectionends $buffer];					# get end of selection
1789	set start [lindex $ends 0];								# line number
1790	set pos [lindex [positiontolineoffset $buffer $start] 1];# get column number
1791	if {$pos>$lineWrapColumn} \
1792	{
1793		InsertAndHome $buffer "\n";							# insert newline
1794	} \
1795	else \
1796	{
1797		InsertAndHome $buffer " ";							# insert space
1798	}
1799}
1800
1801# Changes the status of line-wrapping
1802proc WrapOnOff {} \
1803{
1804	global lineWrap;
1805	set temp "off";
1806	if {$lineWrap} \
1807	{
1808		set temp "on";
1809	}
1810	set lineWrap [yesnodialog "Line wrapping is currently '$temp'\nDo you want to have line wrapping on?"];
1811	if {$lineWrap} \
1812	{
1813		bindkey space		{x0000000000} {WrapLine [ActiveWindowOrBeep]};		# when space is hit, see if above the given column and insert newline if needed
1814	} \
1815	else \
1816	{
1817		unbindkey space		{x0000000000}
1818	}
1819}
1820
1821# Changes the column at which lines wrap
1822proc WrapNewColumn {} \
1823{
1824	global lineWrapColumn;
1825	set lineWrapColumn [expr [textdialog "New line wrap column:" $lineWrapColumn]];		# get new column limit from user
1826}
1827
1828# Get text to evaluate from window, then stick in a newline.
1829proc EvalText {window} \
1830{
1831	set ends [getselectionends $window]
1832	set start [lindex $ends 0]
1833	set end [lindex $ends 1]
1834	if {$start==$end} \
1835	{
1836		set line [lindex [positiontolineoffset $window $start] 0];			# get line we want to select
1837		set start [lineoffsettoposition $window $line 0];					# point to the start
1838		set end [lineoffsettoposition $window [expr $line+1] 0];			# point to the end
1839	}
1840	if {[lindex [positiontolineoffset $window $end] 1]==0} \
1841	{
1842		if {$start!=$end} \
1843		{
1844			incr end -1;													# move back past last newline
1845		}
1846	}
1847	setselectionends $window $start $end;									# make only one selection, even if more than one
1848	set result [lindex [selectedtextlist $window] 0]\n;						# get contents of selection to return (add in new line)
1849	setselectionends $window $end $end;										# move to the end
1850	breakundo $window;														# force a break in the undo stream for this window
1851	InsertAndHome $window "\n";												# add newline
1852	set result;																# return this
1853}
1854
1855# When a shell command arrives, this handles it
1856# NOTE: ShellCommand is a procedure that the editor expects to be defined.
1857# The editor calls this procedure in response to certain events.
1858# The following is a list of the commands that are currently defined:
1859# initialargs	-- sent when the editor begins, args is the list of command line arguments
1860# open			-- editor is asked to open documents, args is path name list
1861# close			-- editor is asked to close documents, args is buffer list
1862# quit			-- editor is asked to quit, args is not used
1863proc ShellCommand {command args} \
1864{
1865	switch $command \
1866	{
1867		"initialargs"
1868		{
1869			OpenList $args;							# later, if we want, we can interpret this for command line switches, or whatever
1870		}
1871		"open"
1872		{
1873			OpenList $args
1874		}
1875		"close"
1876		{
1877			foreach window $args \
1878			{
1879				AskClose $window
1880			}
1881		}
1882		"quit"
1883		{
1884			TryToQuit
1885		}
1886	}
1887}
1888
1889# display the e93 about box
1890proc AboutBox {} \
1891{
1892	global SCRIPTPATH;
1893
1894	okdialog "[version]\nTcl: [info tclversion]\nStartup Script: $SCRIPTPATH\n\n$::tcl_platform(os) $::tcl_platform(osVersion) $::tcl_platform(machine)\nProcess ID: [pid]\n\nNumber of open windows: [llength [windowlist]]\nNumber of open buffers: [llength [bufferlist]]\n\nFor the latest version, visit www.e93.org"
1895}
1896
1897# Add another directory path to the directory menu.
1898proc AddDirectoryMenu {path} \
1899{
1900	addmenu {Directory} LASTCHILD 1 "$path" {} "cd \{$path\}"
1901}
1902
1903# Define the menus.
1904addmenu {} LASTCHILD 1 "e93" "" ""
1905	addmenu {e93} LASTCHILD 1 "About..."						{}				{AboutBox}
1906	addmenu {e93} LASTCHILD 0 "space0"							{\\S}			{}
1907
1908	addmenu {e93} LASTCHILD 1 "Help for e93"					{}				{OpenList [file join $sysPrefsDir README.e93]}
1909	addmenu {e93} LASTCHILD 1 "Help for regular expressions"	{}				{OpenList [file join $sysPrefsDir README.regex]}
1910	addmenu {e93} LASTCHILD 1 "Help for syntax maps"			{}				{OpenList [file join $sysPrefsDir README.syntaxmaps]}
1911
1912addmenu {} LASTCHILD 0 "space0" {\\S}	{}
1913
1914addmenu {} LASTCHILD 1 "File" "" ""
1915	addmenu {File} LASTCHILD 1 "New"							{\\Kn}			{NewWindow}
1916	addmenu {File} LASTCHILD 1 "Open..."						{\\Ko}			{OpenList [opendialog "Open File:"]}
1917	addmenu {File} LASTCHILD 1 "Open Selection"					{\\Kd}			{SmartOpenList [SelectLineWhenNoSelection [set window [ActiveWindowOrBeep]]; selectedtextlist $window]}
1918	addmenu {File} LASTCHILD 1 "Include..."						{}				{IncludeList [set window [ActiveWindowOrBeep]] [opendialog "Include:"];HomeWindowToSelectionStart $window}
1919	addmenu {File} LASTCHILD 0 "space0"							{\\S}			{}
1920	addmenu {File} LASTCHILD 1 "Close"							{\\Kw}			{AskClose [ActiveWindowOrBeep]}
1921	addmenu {File} LASTCHILD 1 "Close All"						{\\KW}			{foreach window [windowlist] {AskClose $window}}
1922	addmenu {File} LASTCHILD 1 "Save"							{\\Ks}			{AskSave [ActiveWindowOrBeep]}
1923	addmenu {File} LASTCHILD 1 "Save As..."						{\\KS}			{AskSaveAs [ActiveWindowOrBeep]}
1924	addmenu {File} LASTCHILD 1 "Save To..."						{}				{AskSaveTo [ActiveWindowOrBeep]}
1925	addmenu {File} LASTCHILD 1 "Save All"						{}				{AskSaveAll}
1926	addmenu {File} LASTCHILD 1 "Revert To Saved"				{}				{AskRevert [ActiveWindowOrBeep]}
1927	addmenu {File} LASTCHILD 0 "space1"							{\\S}			{}
1928	addmenu {File} LASTCHILD 1 "Print..."						{\\Kp}			{PrintBuffer [ActiveWindowOrBeep]}
1929	addmenu {File} LASTCHILD 1 "Print Selection..."				{\\KP}			{PrintSelection [ActiveWindowOrBeep]}
1930	addmenu {File} LASTCHILD 1 "Print Windows..."				{}				{PrintWindows}
1931	addmenu {File} LASTCHILD 0 "space2"							{\\S}			{}
1932	addmenu {File} LASTCHILD 1 "Quit"							{\\Kq}			{TryToQuit}
1933
1934addmenu {} LASTCHILD 1 "Edit" "" ""
1935	addmenu {Edit} LASTCHILD 1 "Undo/Redo Toggle"				{\\Kz}			{if {[undotoggle [set window [ActiveWindowOrBeep]]]!=0} {HomeWindowToSelectionStart $window} else {beep}}
1936	addmenu {Edit} LASTCHILD 1 "Undo"							{\\Ku}			{if {[undo [set window [ActiveWindowOrBeep]]]!=0} {HomeWindowToSelectionStart $window} else {beep}}
1937	addmenu {Edit} LASTCHILD 1 "Redo"							{\\Ky}			{if {[redo [set window [ActiveWindowOrBeep]]]!=0} {HomeWindowToSelectionStart $window} else {beep}}
1938	addmenu {Edit} LASTCHILD 1 "Flush Undo/Redo Buffer"			{}				{okcanceldialog "Really flush undos for:\n'[set window [ActiveWindowOrBeep]]'?";flushundos $window}
1939	addmenu {Edit} LASTCHILD 0 "space0" 						{\\S}			{}
1940	addmenu {Edit} LASTCHILD 1 "Cut"							{\\Kx}			{SmartCut [set window [ActiveWindowOrBeep]] [CurrentClipboardOrBeep];HomeWindowToSelectionStart $window;flushundos [getclipboard]}
1941	addmenu {Edit} LASTCHILD 1 "Copy"							{\\Kc}			{SmartCopy [ActiveWindowOrBeep] [CurrentClipboardOrBeep];flushundos [getclipboard]}
1942	addmenu {Edit} LASTCHILD 1 "Paste"							{\\Kv}			{HomeWindowToSelectionStart [set window [ActiveWindowOrBeep]];paste $window [CurrentClipboardOrBeep];HomeWindowToSelectionStart $window -lenient}
1943	addmenu {Edit} LASTCHILD 1 "Clear"							{}				{clear [set window [ActiveWindowOrBeep]];HomeWindowToSelectionStart $window}
1944	addmenu {Edit} LASTCHILD 0 "space1" 						{\\S}			{}
1945	addmenu {Edit} LASTCHILD 1 "Clipboards"						{}				{}
1946		addmenu {Edit Clipboards} LASTCHILD 1 "Show Current Clipboard..."	{}	{ShowCurrentClipboard}
1947		addmenu {Edit Clipboards} LASTCHILD 1 "Open Current Clipboard..."	{}	{OpenDefaultWindow [CurrentClipboardOrBeep]}
1948		addmenu {Edit Clipboards} LASTCHILD 0 "space0"			{\\S}			{}
1949		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 0"		{\\K0}			{setclipboard clip0}
1950		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 1"		{\\K1}			{setclipboard clip1}
1951		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 2"		{\\K2}			{setclipboard clip2}
1952		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 3"		{\\K3}			{setclipboard clip3}
1953		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 4"		{\\K4}			{setclipboard clip4}
1954		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 5"		{\\K5}			{setclipboard clip5}
1955		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 6"		{\\K6}			{setclipboard clip6}
1956		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 7"		{\\K7}			{setclipboard clip7}
1957		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 8"		{\\K8}			{setclipboard clip8}
1958		addmenu {Edit Clipboards} LASTCHILD 1 "Clipboard 9"		{\\K9}			{setclipboard clip9}
1959	addmenu {Edit} LASTCHILD 0 "space2" 						{\\S}			{}
1960	addmenu {Edit} LASTCHILD 1 "Select All"						{\\Ka}			{selectall [ActiveWindowOrBeep]}
1961	addmenu {Edit} LASTCHILD 1 "Unselect Whitespace"			{\\Kb}			{UnselectWhitespace [ActiveWindowOrBeep]}
1962	addmenu {Edit} LASTCHILD 0 "space3" 						{\\S}			{}
1963	addmenu {Edit} LASTCHILD 1 "Align Left"						{\\Kbraceleft}	{AlignLeft [ActiveWindowOrBeep]}
1964	addmenu {Edit} LASTCHILD 1 "Shift Left"						{\\Kbracketleft}	{ShiftLeft [ActiveWindowOrBeep]}
1965	addmenu {Edit} LASTCHILD 1 "Shift Right"					{\\Kbracketright}	{ShiftRight [ActiveWindowOrBeep]}
1966
1967addmenu {} LASTCHILD 1 "Find" "" ""
1968	addmenu {Find} LASTCHILD 1 "Find/Replace..."				{\\Kf}			{AskSearch [ActiveWindowOrBeep]}
1969	addmenu {Find} LASTCHILD 1 "Find/Replace in All..."			{}				{ActiveWindowOrBeep;AskSearchAll}
1970	addmenu {Find} LASTCHILD 1 "Find Same Backwards"			{\\KG}			{FindNext [ActiveWindowOrBeep] 1}
1971	addmenu {Find} LASTCHILD 1 "Find Same"						{\\Kg}			{FindNext [ActiveWindowOrBeep] 0}
1972	addmenu {Find} LASTCHILD 1 "Find Selection Backwards"		{\\KH}			{FindSelectionNext [ActiveWindowOrBeep] 1}
1973	addmenu {Find} LASTCHILD 1 "Find Selection"					{\\Kh}			{FindSelectionNext [ActiveWindowOrBeep] 0}
1974	addmenu {Find} LASTCHILD 0 "space0" 						{\\S}			{}
1975	addmenu {Find} LASTCHILD 1 "Replace Same Backwards"			{\\KT}			{ReplaceNext [ActiveWindowOrBeep] 1}
1976	addmenu {Find} LASTCHILD 1 "Replace Same"					{\\Kt}			{ReplaceNext [ActiveWindowOrBeep] 0}
1977	addmenu {Find} LASTCHILD 0 "space1" 						{\\S}			{}
1978	addmenu {Find} LASTCHILD 1 "Replace Selections With..."		{\\KR}			{ReplaceSelections [ActiveWindowOrBeep]}
1979	addmenu {Find} LASTCHILD 0 "space2" 						{\\S}			{}
1980	addmenu {Find} LASTCHILD 1 "Go To Line..."					{\\Kl}			{selectline [set window [ActiveWindowOrBeep]] [expr [textdialog "Go to line:"]];HomeWindowToSelectionStart $window}
1981	addmenu {Find} LASTCHILD 1 "Locate Selection"				{\\KL}			{HomeWindowToSelectionStart [ActiveWindowOrBeep] -strict}
1982	addmenu {Find} LASTCHILD 0 "space3" 						{\\S}			{}
1983	addmenu {Find} LASTCHILD 1 "Grep -n For..."					{\\KF}			{set data "grep -E -n -s --line-buffered -D skip [set grepFilesChoice [textdialog "Enter pattern, and file list: (eg. void *.c)" $grepFilesChoice]]"; set window [NewWindow]; task $window $data}
1984
1985addmenu {} LASTCHILD 1 "Window" "" ""
1986	addmenu {Window} LASTCHILD 1 "Tcl console"					{}				{OpenDefaultWindow tclConsole}
1987	addmenu {Window} LASTCHILD 0 "space0"						{\\S}			{}
1988	addmenu {Window} LASTCHILD 1 "Get Information"				{\\Ki}			{BufferInfo [ActiveWindowOrBeep]}
1989	addmenu {Window} LASTCHILD 1 "Set Font..."					{\\KE}			{setfont [set window [ActiveWindowOrBeep]] [fontdialog "Choose Font:" [getfont $window]]}
1990	addmenu {Window} LASTCHILD 1 "Set Tab Size..."				{\\Ke}			{settabsize [set window [ActiveWindowOrBeep]] [expr [textdialog "New tab size:" [gettabsize $window]]]}
1991	addmenu {Window} LASTCHILD 0 "space1"						{\\S}			{}
1992	addmenu {Window} LASTCHILD 1 "Swap Top Windows"				{\\Kspace}		{SwapWindows}
1993	addmenu {Window} LASTCHILD 1 "Rotate Windows"				{\\Kgrave}		{RotateWindows}
1994	addmenu {Window} LASTCHILD 1 "Choose Window..."				{\\KO}			{ChooseWindow}
1995	addmenu {Window} LASTCHILD 0 "space2"						{\\S}			{}
1996	addmenu {Window} LASTCHILD 1 "Stack Windows"				{}				{StackWindows}
1997	addmenu {Window} LASTCHILD 1 "Tile Windows"					{}				{TileWindows}
1998	addmenu {Window} LASTCHILD 0 "space3"						{\\S}			{}
1999	addmenu {Window} LASTCHILD 1 "Language Mode"				{}				{}
2000		addmenu {Window "Language Mode"} LASTCHILD 1 "<none>"	{}				{SetHighlightMode [ActiveWindowOrBeep] ""}
2001	foreach element [lsort -dictionary [syntaxmaps]] \
2002	{
2003		addmenu {Window "Language Mode"} LASTCHILD 1 $element	{}				"SetHighlightMode \[ActiveWindowOrBeep\] $element"
2004	}
2005	addmenu {Window} LASTCHILD 1 "Color Scheme"					{}				{}
2006	foreach element [lsort -dictionary [array names HighlightSchemes]] \
2007	{
2008		addmenu {Window "Color Scheme"} LASTCHILD 1 $element	{}				"SetHighlightScheme \[ActiveWindowOrBeep\] \"$element\""
2009	}
2010
2011addmenu {} LASTCHILD 1 "Mark" "" ""
2012	addmenu {Mark} LASTCHILD 1 "Mark..."						{}				{setmark [ActiveWindowOrBeep] [textdialog "Mark selection with what name?"]}
2013	addmenu {Mark} LASTCHILD 1 "Unmark..."						{}				{foreach mark [listdialog "Delete which mark(s):" [marklist [set window [ActiveWindowOrBeep]]]] {closemark $window $mark}}
2014	addmenu {Mark} LASTCHILD 1 "Goto Mark..."					{}				{foreach mark [listdialog "Go to which mark:" [marklist [set window [ActiveWindowOrBeep]]]] {gotomark $window $mark; HomeWindowToSelectionStart $window -strict}}
2015
2016addmenu {} LASTCHILD 1 "Directory" "" ""
2017	addmenu {Directory} LASTCHILD 1 "Show Current Directory"	{}				{okdialog "Current directory:\n\n[pwd]"}
2018	addmenu {Directory} LASTCHILD 1 "Set Directory..."			{}				{set path [pathdialog "Choose directory:"]; cd "$path"; AddDirectoryMenu "$path"}
2019	addmenu {Directory} LASTCHILD 1 "space0"					{\\S}			{}
2020
2021addmenu {} LASTCHILD 1 "Misc" "" ""
2022	addmenu {Misc} LASTCHILD 1 "Strip EOL Whitespace"			{}				{set numRemoved [StripWhite [set window [ActiveWindowOrBeep]]];okdialog "$window\n\nCharacters removed: $numRemoved"}
2023	addmenu {Misc} LASTCHILD 1 "Set Unix LF Line Termination"	{}				{SetLineTermination [ActiveWindowOrBeep] "\n"}
2024	addmenu {Misc} LASTCHILD 1 "Set Mac CR Line Termination"	{}				{SetLineTermination [ActiveWindowOrBeep] "\r"}
2025	addmenu {Misc} LASTCHILD 1 "Set PC CRLF Line Termination"	{}				{SetLineTermination [ActiveWindowOrBeep] "\r\n"}
2026
2027	addmenu {Misc} LASTCHILD 1 "In All Open Windows ..."		{}				{}
2028	addmenu {Misc "In All Open Windows ..."} LASTCHILD 1 "Strip EOL Whitespace"			{}	{ActiveWindowOrBeep;set numRemoved 0;foreach window [windowlist] {incr numRemoved [StripWhite $window]};okdialog "Total characters removed: $numRemoved"}
2029	addmenu {Misc "In All Open Windows ..."} LASTCHILD 1 "Set Unix Line Termination"	{}	{ActiveWindowOrBeep;foreach window [windowlist] {SetLineTermination $window "\n"};okdialog "All windows completed"}
2030	addmenu {Misc "In All Open Windows ..."} LASTCHILD 1 "Set Mac Line Termination"		{}	{ActiveWindowOrBeep;foreach window [windowlist] {SetLineTermination $window "\r"};okdialog "All windows completed"}
2031	addmenu {Misc "In All Open Windows ..."} LASTCHILD 1 "Set PC Line Termination"		{}	{ActiveWindowOrBeep;foreach window [windowlist] {SetLineTermination $window "\r\n"};okdialog "All windows completed"}
2032
2033	addmenu {Misc} LASTCHILD 1 "Uppercase Selection"			{}				{UppercaseSelection [ActiveWindowOrBeep]}
2034	addmenu {Misc} LASTCHILD 1 "Lowercase Selection"			{}				{LowercaseSelection [ActiveWindowOrBeep]}
2035	addmenu {Misc} LASTCHILD 1 "Increment Selected Numbers..."	{}				{IncrementSelection [ActiveWindowOrBeep] [textdialog "How much to increment:" 1]}
2036	addmenu {Misc} LASTCHILD 1 "Enumerate Selections..."		{}				{EnumerateSelection [ActiveWindowOrBeep] [textdialog "Starting value:" 1] [textdialog "How much to increment:" 1]}
2037	addmenu {Misc} LASTCHILD 1 "Sum of Selected Numbers"		{}				{SumSelection [ActiveWindowOrBeep]}
2038	addmenu {Misc} LASTCHILD 1 "Sort Selection Segments"		{}				{SortSelection [ActiveWindowOrBeep]}
2039	addmenu {Misc} LASTCHILD 1 "Reverse Selection Segments"		{}				{ReverseSelection [ActiveWindowOrBeep]}
2040	addmenu {Misc} LASTCHILD 1 "Refill Selection..."			{\\Kr}			{RefillSelection [ActiveWindowOrBeep]}
2041	addmenu {Misc} LASTCHILD 1 "Line Wrapping..."				{}				{WrapOnOff}
2042	addmenu {Misc} LASTCHILD 1 "Get man Page..."				{\\Kslash}		{ManPage [textdialog "Enter man page subject:"]}
2043	addmenu {Misc} LASTCHILD 1 "Spell"							{}				{SpellDocument [ActiveWindowOrBeep]}
2044	addmenu {Misc} LASTCHILD 1 "Mail To..."						{}				{MailDocument [ActiveWindowOrBeep]}
2045	addmenu {Misc} LASTCHILD 1 "Mail Windows To..."				{}				{MailWindows}
2046
2047addmenu {} LASTCHILD 1 "Tasks" "" ""
2048	addmenu {Tasks} LASTCHILD 1 "Execute Shell Task"			{\\KKP_Enter}	{task [set window [ActiveWindowOrBeep]] [EvalText $window]}
2049	addmenu {Tasks} LASTCHILD 1 "Send EOF To Task"				{\\Kk}			{eoftask [ActiveWindowOrBeep]}
2050	addmenu {Tasks} LASTCHILD 1 "Kill Task"						{\\KK}			{killtask [ActiveWindowOrBeep]}
2051	addmenu {Tasks} LASTCHILD 0 "space0"						{\\S}			{}
2052	addmenu {Tasks} LASTCHILD 1 "Pipe Selections Through..."	{}				{PipeSelection [ActiveWindowOrBeep]}
2053
2054addmenu {} LASTCHILD 1 "Build" "" ""
2055	addmenu {Build} LASTCHILD 1 "make"							{\\Km}			{HomeWindowToSelectionStart [set window [ActiveWindowOrBeep]];breakundo $window;task $window make}
2056
2057# Bind keys to various useful things.
2058# l=caps lock, s=shift, c=control, 0-7 are additional modifiers such as command, alt, option, command, etc...
2059# Bind flags		 lsc01234567 (x means don't care, 0 means not pressed, 1 means pressed)
2060
2061bindkey F1			{x0010000000} {setmark [set window [ActiveWindowOrBeep]] F1;markvisible $window F1 008000}
2062bindkey F1			{x1010000000} {orselection [ActiveWindowOrBeep] -destmark F1}
2063bindkey F1			{x0000000000} {gotomark [set window [ActiveWindowOrBeep]] F1;HomeWindowToSelectionStart $window -semistrict}
2064bindkey F2			{x0010000000} {setmark [set window [ActiveWindowOrBeep]] F2;markvisible $window F2 800000}
2065bindkey F2			{x1010000000} {orselection [ActiveWindowOrBeep] -destmark F2}
2066bindkey F2			{x0000000000} {gotomark [set window [ActiveWindowOrBeep]] F2;HomeWindowToSelectionStart $window -semistrict}
2067bindkey F3			{x0010000000} {setmark [set window [ActiveWindowOrBeep]] F3;markvisible $window F3 6060A0}
2068bindkey F3			{x1010000000} {orselection [ActiveWindowOrBeep] -destmark F3}
2069bindkey F3			{x0000000000} {gotomark [set window [ActiveWindowOrBeep]] F3;HomeWindowToSelectionStart $window -semistrict}
2070bindkey F4			{x0010000000} {setmark [set window [ActiveWindowOrBeep]] F4;markvisible $window F4 007070}
2071bindkey F4			{x1010000000} {orselection [ActiveWindowOrBeep] -destmark F4}
2072bindkey F4			{x0000000000} {gotomark [set window [ActiveWindowOrBeep]] F4;HomeWindowToSelectionStart $window -semistrict}
2073
2074bindkey F5			{x0000000000} {WrapOnOff};
2075bindkey F5			{x0010000000} {WrapNewColumn};
2076
2077bindkey KP_Enter	{x0000000000} {execute [set window [ActiveWindowOrBeep]] [EvalText $window];catch {HomeWindowToSelectionStart $window -lenient}}
2078bindkey	Return		{x0010000000} {task [set window [ActiveWindowOrBeep]] [EvalText $window]}; # this is for laptop machines without a keypad
2079bindkey Help		{x0000000000} {ManPage [textdialog "Enter man page subject:"]}
2080
2081# Specify initial paths to be placed into the directory menu.
2082
2083AddDirectoryMenu /
2084catch {AddDirectoryMenu [glob ~]};		# this can fail if user has no home directory
2085AddDirectoryMenu [pwd]
2086
2087
2088# see if the user has his own set of modules specified, if so, skip including the defaults, and get only his
2089if {[file exists [file join $userPrefsDir $modulesDir]]} \
2090{
2091	foreach file [lsort [glob -nocomplain [file join $userPrefsDir $modulesDir *.tcl]]] \
2092	{
2093		source $file;
2094	}
2095} \
2096else \
2097{
2098	# use all system defined modules
2099	foreach file [lsort [glob -nocomplain [file join $sysPrefsDir $modulesDir *.tcl]]] \
2100	{
2101		source $file;
2102	}
2103}
2104
2105# get user's additional modules (if any)
2106if {[file exists [file join $userPrefsDir $auxModulesDir]]} \
2107{
2108	foreach file [lsort [glob -nocomplain [file join $userPrefsDir $auxModulesDir *.tcl]]] \
2109	{
2110		source $file;
2111	}
2112}
2113
2114# get user's preferences (if any)
2115if {[file exists [file join $userPrefsDir $prefsDir]]} \
2116{
2117	foreach file [lsort [glob -nocomplain [file join $userPrefsDir $prefsDir *.tcl]]] \
2118	{
2119		source $file;
2120	}
2121}
2122