GnoclEdit.tcl -Plain Text Editor Using Gnocl Tcl-Gtk bindings

WJG 29/10/07 Here's a simple text editor in Gnocl which I put together this morning. I shouldn't have really; I ought to have been working on my thesis. But hey, who is watching me? Whilst there are some deficiencies in the current release (0.9.9.1) of the text widget such as no undo function or a tags dump (perhaps more a lack in Gtk than goncl itself), there is still much that has to be lauded. For instance, Tooltips are a basic option, formatted strings can be used in widget labels, scroll-bars automatically appear for large text files and accellerators for common commands are set up for us. So, cut, paste and play...

WJG (25/11/08) Well, we're now onto Gnocl 0.9.92 and we have more functions to play with. Text tag dumps are due in 0.9.93. Text undo/redo is now possible via Tcl side scripting, for more on that follow this link Undo/Redo for Gnocl Text Widgets.

Here's a screen shot:

http://wjgiddings.googlepages.com/Screenshot-GnoclEdit.png


 #---------------
 # GnoclEdit.tcl
 #
 # Simple, plain text editor using gnocl Tcl-Gtk bindings
 # Created by William J Giddings
 # 29-October-2007
 #---------------

 #!/bin/sh
 # the next line restarts using tclsh \
 exec tclsh "$0" "$@"

 package require Gnocl

 # array to contain some global variables
 set GE(text)     ""           ;# name of editor widget
 set GE(status)   ""           ;# name of status bar widget
 set GE(filename) "untitled"   ;# filename
 set GE(basefont) "Courier"    ;# besefont for text display
 set GE(fontsize) "12"         ;# default font size in pixels

 # array to contain tooltip strings
 set TT(new)      "Clear workspace and begin a new document."
 set TT(open)     "Clear existing workspace and open an existing file for editing."
 set TT(save)     "Save the current, named file to disk."
 set TT(saveas)   "Save an untitled file to disk or save a copy with a new name."
 set TT(quit)     "Discard current document and close the GnoclEdit program."
 set TT(print)    "Print the contents of the edit buffer."
 set TT(cut)      "Cut the active selection to the clipboard."
 set TT(copy)     "Copy the active selection to the clipboard."
 set TT(paste)    "Paste the current clipboard into the insertion point or replace selection."
 set TT(delete)   "Delete the current selection from the document."
 set TT(selall)   "Select all text within the current document."

 #
 # build the application user interface
 #
 proc main {} {
   # Step 1) create menus
   # a) file
   set menu [gnocl::menu]
   $menu add [gnocl::menuItem -text "%#New" -tooltip $::TT(new) \
      -onClicked fileNew]
   $menu add [gnocl::menuItem -text "%#Open" -tooltip $::TT(open) \
      -onClicked fileOpen]
   $menu add [gnocl::menuItem -text "%#Save" -tooltip $::TT(save) \
      -onClicked fileSave]
   $menu add [gnocl::menuItem -text "%#SaveAs" -tooltip $::TT(saveas) \
      -onClicked fileSaveAs]
   $menu add [gnocl::menuSeparator]
   $menu add [gnocl::menuItem -text "%#Print" -tooltip $::TT(print) \
      -onClicked print]
   $menu add [gnocl::menuSeparator]
   $menu add [gnocl::menuItem -text "%#Quit"  -tooltip $::TT(quit) \
      -onClicked exit ]

   set file [gnocl::menuItem -text "%__File" -submenu $menu]

   # b) edit
   set edit [gnocl::menu]
   $edit add [gnocl::menuItem -text "%#Cut" -tooltip $::TT(cut) \
      -onClicked cut ]
   $edit add [gnocl::menuItem -text "%#Copy" -tooltip $::TT(copy) \
      -onClicked copy ]
   $edit add [gnocl::menuItem -text "%#Paste" -tooltip $::TT(paste) \
      -onClicked paste ]
   $edit add [gnocl::menuSeparator]
   $edit add [gnocl::menuItem -text "%#Delete" -tooltip $::TT(delete) \
      -onClicked delete ]
   $edit add [gnocl::menuSeparator]
   $edit add [gnocl::menuItem -text "%_Select _All" -tooltip $::TT(selall) \
      -onClicked selectAll -accelerator "<Ctrl>A" ]


   set edit [gnocl::menuItem -text "%__Edit" -submenu $edit]

   # c) about
   set menu [gnocl::menu]
   $menu add [gnocl::menuItem -text "%__About" \
      -tooltip "Show about dialog" \
      -onClicked about ]
   set help [gnocl::menuItem -text "%__Help" -submenu $menu]

   # Step 2) create toolbar
   set toolBar [gnocl::toolBar -style both]
   $toolBar add item -text "%#New" -tooltip $::TT(new) \
      -onClicked fileNew
   $toolBar add item -text "%#Open" -tooltip $::TT(open) \
      -onClicked fileOpen
   $toolBar add item -text "%#Save" -tooltip $::TT(save) \
      -onClicked fileSave
   $toolBar add space
   $toolBar add item -text "%#Print" -tooltip $::TT(print) \
              -onClicked print
   $toolBar add space
   $toolBar add item -text "%#Cut" -tooltip $::TT(cut) \
      -onClicked cut
   $toolBar add item -text "%#Copy" -tooltip $::TT(copy) \
      -onClicked copy
   $toolBar add item -text "%#Paste" -tooltip $::TT(paste) \
      -onClicked paste

   # Step 3) create text edit area, and default formatting tag
   # No need to add scrollbars or input popumenu, these will be added automatically

   set text [gnocl::text \
                   -baseFont [list $::GE(basefont) $::GE(fontsize)] \
                -onButtonPress {
                   puts %b
                   if {%b == "3" } { textPopup }  \
                 }]

   $text tag create default -fontFamily $::GE(basefont) -fontSize $::GE(fontsize)

   # Step 4) create status bar
   set status [gnocl::statusBar]

   # Step 5) create Container and toplevel window, add containter to window
   set box [gnocl::box -orientation vertical -borderWidth 0 -spacing 0]
   set win [gnocl::window -child $box -title "GnoclEdit" -onDelete exit]

   # Step 6) add elements to the container
   $box add [gnocl::menuBar -children [list $file $edit $help]]
   $box add $toolBar
   $box add $text -fill {1 1} -expand 1
   $box add $status

   # Step 7) update globals
   set ::GE(text) $text
   set ::GE(status) $status

   # Step 8) default status
   $::GE(status) push "File: $::GE(filename)"

   # enter the main input loop
   gnocl::mainLoop
 }

 #---------------
 # update the status bar, remind us of the filename!
 #---------------
 proc statusUpdate {str} { $::GE(status) push "File: $str" }

 #---------------
 # file menu procs, they speak for themselves
 #---------------
 proc fileNew {} {
   $::GE(text) erase {0 0} end
   set ::GE(filename) "untitled"
   statusUpdate "untitled"
 }

 proc fileOpen {} {
   set file [gnocl::fileChooser \
      -action open \
      -title "Please choose a file to edit.."]

   if { $file != "" } {
      set fp [open $file "r"]
      set data [read $fp]
      close $fp
      $::GE(text) erase {0 0} end
      $::GE(text) insert end $data -tags default
      statusUpdate $file
      set ::GE(filename) $file
   }
 }

 proc fileSaveAs {} {
   set file [gnocl::fileChooser \
      -action save \
      -title "Please choose a file to save.."]

   if { $file != "" } {
      set fp [open $file "w"]
      set data [$::GE(text) get {0 0} end-1]
      puts $fp $data
      close $fp
      statusUpdate $file
      set ::GE(filename) $file
   }
 }

 proc fileSave {} {

   if {$::GE(filename) != "untitled" } {
      set fp [open $::GE(filename) "w"]
      set data [$::GE(text) get {0 0} end-1]
      puts $fp $data
      close $fp
      return
   } else {
      fileSaveAs
   }

 }

 proc print {} {
    set fp [open __print.tmp__ "w"]
    set data [$::GE(text) get {0 0} end-1]
    puts $fp $data
    close $fp
    exec lpr __print.tmp__ &
 }

 # Basically rebuilds the menubar submenu 
 proc textPopup {} {
   # make text popup menu
   set editPopup [gnocl::menu -tearoff 0]
   $editPopup add [gnocl::menuItem -text "%#Cut" -tooltip $::TT(cut) \
      -onClicked cut ]
   $editPopup add [gnocl::menuItem -text "%#Copy" -tooltip $::TT(copy) \
      -onClicked copy ]
   $editPopup add [gnocl::menuItem -text "%#Paste" -tooltip $::TT(paste) \
      -onClicked paste ]
   $editPopup add [gnocl::menuSeparator]
   $editPopup add [gnocl::menuItem -text "%#Delete" -tooltip $::TT(delete) \
      -onClicked delete ]
   $editPopup add [gnocl::menuSeparator]
   $editPopup add [gnocl::menuItem -text "%_Select _All" -tooltip $::TT(selall) \
      -onClicked selectAll -accelerator "<Ctrl>A" ]
   $editPopup popup
 }

 #---------------
 # clipboard operations
 #---------------
 proc cut {} {
   gnocl::clipboard clear
   gnocl::clipboard setText [$::GE(text) get selectionStart selectionEnd ]
   $::GE(text) erase selectionStart selectionEnd
 }

 proc paste {} {
    $::GE(text) erase selectionStart selectionEnd
    $::GE(text) insert cursor [gnocl::clipboard getText] -tags default
 }

 proc copy {} { gnocl::clipboard setText [$::GE(text) get selectionStart selectionEnd ] }

 proc delete {} { $::GE(text) erase selectionStart selectionEnd }

 proc selectAll {} { $::GE(text) select {0 0} end-1 }

 #---------------
 # about box
 #---------------
 proc about {} { gnocl::dialog -type info -text "GnoclEdit by\nWilliam J Giddings\n29-Oct-2007" }

 # RUN THE APP!
 main

tb 11-05-2007 - Nice and clean code, but how does one get the edit menu to popup on mouse button 3?

wjg (16/11/07) Nice and clean eh? Thanks. The current release of GNOCL doesn't support additional button event mappings. But, it is possible. Peter Baum, the creator of GNOCL has taken me through the process of adding further events to the gnocl::text widget and so far so good. Button-3 event handling is possible, but don't forget that the GTK+ text widget already has default binding for Button-3 which supports the selection of input methods. If you wish, I can send you a copy of the version as it stands for you to expirement with. The next areas functionality to add to the text widget are tag bindings and tag dumping. It should be available soon.

tb 11-22-2007 - Well, to me the concept of programmable context menus is tight bound to the concept of object orientation. Having an object, like a database record content, as a textual representation inside a text widget doesn't really need Copy/Cut/Paste in its context, but instead it'd need a context menu giving options like Create/Edit/Delete. I think, when it come to OO for the user, then it goes like "what you see is an object and what it can do for you lies in its context menu". But perhaps I'm too dogmatic?

wjg (25/03/08) I'm not certain what you're asking for, but the default Gnome popup menu does provide clipboard operations which work.


wjg (25/11/08) tb, in reponse your question about popup menus, I've modified the above script to respond to a Button-3 press. I've also added some simple printing too.