Arjen Markus (12 august 2003) I wanted a simple, basic editor for young, inexperienced programmers. The thing is: any editor I know of is too general or too limited or has quirks that I do not fancy. So, what is easier than writing an editor yourself? After asking around a bit, I came up with this script.
Note:
Most importantly: it contains some Windows-specific settings, as I wanted it to look as good as I could get it.
# ypedit.tcl -- # Shell procedures for Young Programmers Project: # It defines several utility commands: # ed - edit a file # vw - view the contents of a file # package require Tk # ypshell -- # Namespace for the commands # namespace eval ::ypshell { namespace export ed vw variable editfile set editfile(count) 0 } # ed -- # Edit a file # # Arguments: # filename Name of the file to edit # # Return value: # None # proc ::ypshell::ed { filename } { ::ypshell::EditFile 1 0 $filename } # vw -- # View a file # # Arguments: # filename Name of the file to view # # Return value: # None # proc ::ypshell::vw { filename } { ::ypshell::EditFile 0 0 $filename } # EditFile -- # Edit or view a file (actual procedure) # # Arguments: # editable Select edit mode (1=edit, 0=view) # newopen Allow new/open menu items (1=yes, 0=no) # filename Name of the file to edit/view # # Return value: # None # proc ::ypshell::EditFile { editable newopen filename } { variable editfile incr editfile(count) set w .edit$editfile(count) toplevel $w wm withdraw . wm title $w "Edit/view: $filename" set mb $w.menubar frame $mb pack $mb -side top -fill x -pady 0 menubutton $mb.file -text File -menu $mb.file.menu -underline 0 menubutton $mb.edit -text Edit -menu $mb.edit.menu -underline 0 menubutton $mb.search -text Search -menu $mb.search.menu -underline 0 menubutton $mb.help -text Help -menu $mb.help.menu -underline 0 menu $mb.file.menu -tearoff false menu $mb.edit.menu -tearoff false menu $mb.search.menu -tearoff false menu $mb.help.menu -tearoff false pack $mb.file $mb.edit $mb.search -side left pack $mb.help -side left ;# Platform-dependent! # # Set up the "File" menu # if { $newopen } { if { $editable } { $mb.file.menu add command -label New -underline 0 \ -command [list ::ypshell::NewFile] } $mb.file.menu add command -label Open -underline 0 \ -command [list ::ypshell::OpenFile] $mb.file.menu add separator } if { $editable } { $mb.file.menu add command -label Save -underline 0 \ -command [list ::ypshell::SaveFile $w $filename 0] if { $newopen } { $mb.file.menu add command -label "Save as ..." -underline 1 \ -command [list ::ypshell::SaveFile $w $filename 1] } $mb.file.menu add separator } $mb.file.menu add command -label Exit -underline 1 \ -command [list ::ypshell::ExitEdit $w $newopen $editable $filename] # # Set up the "Edit" menu # if { $editable } { $mb.edit.menu add command -label "Undo" -underline 0 \ -command [list ::ypshell::UndoChange] $mb.edit.menu add command -label "Redo" -underline 0 \ -command [list ::ypshell::RedoChange] $mb.edit.menu add separator } $mb.edit.menu add command -label "Mark line(s)" -underline 0 \ -command [list ::ypshell::MarkLines] $mb.edit.menu add command -label "Unmark line(s)" -underline 1 \ -command [list ::ypshell::UnmarkLines] $mb.edit.menu add separator $mb.edit.menu add command -label "Copy" -underline 0 \ -command [list ::ypshell::CopyBlock] if { $editable } { $mb.edit.menu add command -label "Paste" -underline 0 \ -command [list ::ypshell::PasteBlock] $mb.edit.menu add separator $mb.edit.menu add command -label "Delete" -underline 0 \ -command [list ::ypshell::DeleteBlock] } # # Set up the "Search" menu # $mb.search.menu add command -label "Find" -underline 0 \ -command [list ::ypshell::FindText $w] if { $editable } { $mb.search.menu add command -label "Replace" -underline 0 \ -command [list ::ypshell::ChangeText] } # # Set up the "Help" menu # $mb.help.menu add command -label "Overview" -underline 1 \ -command [list ::ypshell::HelpOverview] $mb.help.menu add command -label "About" -underline 0 \ -command [list ::ypshell::AboutEdit] # # Create a thin separator # set ts $w.separator frame $ts -height 2 -relief sunken -borderwidth 1 pack $ts -side top -fill x # # Create the toolbar # set fnt "Courier, 10" set fnt "systemfixed" set fixedfont "systemfixed" set tb $w.toolbar frame $tb -height 10 button $tb.goto -text "Go to:" -command [list ::ypshell::GotoLine $w ""] entry $tb.lineno -textvariable ::ypshell::editfile(lineno,$w) \ -width 5 -font $fnt button $tb.top -text "Top" -command [list ::ypshell::GotoLine $w 1] button $tb.bottom -text "Bottom" -command [list ::ypshell::GotoLine $w end] label $tb.empty1 -text " " label $tb.empty2 -text " " set ::ypshell::editfile(lineno,$w) 1 pack $tb.goto $tb.lineno $tb.empty1 $tb.top $tb.bottom $tb.empty2 -side left button $tb.find -text "Find:" -command [list ::ypshell::FindText $w] entry $tb.string -textvariable ::ypshell::editfile(find,$w) \ -font $fnt set ypshell::editfile(find,$w) "" pack $tb.goto $tb.find $tb.string -side left pack $tb -fill x -side top # # Create the text widget and the scroll bars # set tf $w.textframe set tw $tf.text frame $tf scrollbar $tf.scrollx -orient horiz -command "$tw xview" scrollbar $tf.scrolly -command "$tw yview" text $tw -yscrollcommand "$tf.scrolly set" \ -xscrollcommand "$tf.scrollx set" \ -font $fixedfont grid $tw $tf.scrolly grid $tf.scrollx x grid $tw -sticky news grid $tf.scrolly -sticky ns grid $tf.scrollx -sticky ew grid columnconfigure $tf 0 -weight 1 grid rowconfigure $tf 0 -weight 1 $tw configure -wrap none pack $tf -fill both -side top -expand 1 set ypshell::editfile(textwidget,$w) $tw LoadFile $tw $filename } # LoadFile -- # Load the file into the text widget # # Arguments: # textw Text widget to use # filename Name of the file to edit/view # # Return value: # None # proc ::ypshell::LoadFile { textw filename } { set infile [ open "$filename" "r" ] while { ! [ eof $infile ] } { gets $infile line $textw insert end $line $textw insert end "\n" } close $infile } # SaveFile -- # Save the file (possibly under a different name) # # Arguments: # w Main widget holding the text # filename Name of the file to edit/view # newname Ask for a new name or not # # Return value: # None # proc ::ypshell::SaveFile { w filename newname } { variable editfile set tw $editfile(textwidget,$w) # Select a new file name -- TODO set outfile [ open "$filename" "w" ] set lineno 1 while { [$tw compare $lineno.0 < end] } { puts $outfile [$tw get "$lineno.0" "$lineno.0 lineend"] incr lineno } close $outfile } # GotoLine -- # Go to a specified line number # # Arguments: # w Main widget containing the text widget # pos Position # # Return value: # None # proc ::ypshell::GotoLine { w pos } { variable editfile set tw $editfile(textwidget,$w) if { $pos == "" } { set pos $editfile(lineno,$w) } if { $pos == "end" } { $tw mark set current "end linestart" } else { $tw mark set current "$pos.0" } $tw mark set insert [$tw index current] $tw see current focus $tw } # FindText -- # Find a text string # # Arguments: # w Main widget containing the text widget # # Return value: # None # proc ::ypshell::FindText { w } { variable editfile set string $editfile(find,$w) set tw $editfile(textwidget,$w) if { $string != "" } { set newpos [$editfile(textwidget,$w) search -forwards -exact -- $string "insert + 1 chars"] if { $newpos != "" } { $tw mark set current $newpos $tw mark set insert [$tw index current] $tw see current focus $tw } } } # ExitEdit -- # Exit and save the file (if wanted) # # Arguments: # w Widget from which it is called # editable Select edit mode (1=edit, 0=view) # newopen Allow new/open menu items (1=yes, 0=no) # filename Name of the file to edit/view # # Return value: # None # proc ::ypshell::ExitEdit { w editable newopen filename } { if { $editable } { if { !$newopen } { SaveFile $w $filename 0 } else { # Ask whether to save or not } } destroy $w } # # Simple test code # namespace import ::ypshell::ed namespace import ::ypshell::vw ed [file join [file dirname [info script]] ypedit.tcl] vw [file join [file dirname [info script]] ypedit.tcl]
(2003-08-12): minor change -- made the calls at the end relative to the script location. (Later) added wm withdraw . to get rid of the empty toplevel.
AM There is a rather annoying flaw in my way of setting up the menu bar and the menus - this causes most of the grieve (as pointed out by DKF): you better use the menu command, not the frame and menubars that I originally used.
Paul Kienzle (2003-10-12): changed -fill x to -fill both so text box resizes
See also A minimal editor for short code that might be educational reading for kids and notebook editor