GRIDPLUS2 - Example 7
Home Reference Manpage Examples Download License Contact

Example Of a Complete Simple Application

This is an example of a complete text editor application. This example is mainly intended to demonstrate the GRIDPLUS entry/text clipboard commands, text find facilities, enhanced menu options, window resizing and using (ICONS) icon image files.

This example assumes familiarity with the previous/earlier examples. The more basic GRIDPLUS usage is not described here in full.

Note: PNG icon images require Tcl/Tk 8.6.0 or later. GIF images are supported for Tcl/Tk 8.3 or greater.

User Guide

Main Window:

The toolbar contains six buttons, each has appropriate pop-up/balloon help:-

Start a New file.
Open an existing file.
Save file.
Cut selected text.
Copy selected text.
Paste text from clipboard.
Find text matching string entered into entry to the right of this button.

The following menus are available from the menubar:-

The function of the various options follow general conventions and will not be described here.

The Status shown at the bottom right of the display can have the following values:-

NewNew edit - Not yet saved.
OpenedFile opened - Not yet modified.
ModifiedNew edit or Opened file - Modified but not saved since modification.
SavedModifications have been saved.


When a file is opened/saved a screen similar to the following will be displayed:-


The display can be resized in the normal way. A limit is set for the minimum size to which he display can be re-sized (See Comments below) - However, the window can be resized to the maximum allowed by the display.

When the window is reszied to the minimum a screen similar to the following will be displayed:-


If an attempt to Quit the editor, Start a new edit -or- Open a new file while there is a Modified (Unsaved) edit, a dialog similar to the following will be displayed:-


Source Code

#==========Create/display GUI.
proc main {} {
   gridplus menu . {
      _File {
         {_New       ~new}
         {_Open      ~openfile}
         {_Save      ~save}
         {"Save _As" ~saveas}
         =
         {_Quit      ~quit}
      }
      _Edit {
         {Cu_t       ~gpcut}
         {_Copy      ~gpcopy}
         {_Paste     ~gppaste}
         {_Delete    ~gpclear}
         =
         {_Find     "~gpfind_dialog .text"}
         =
         {"_Select All" ~selectall}
      }
      _Help {
         {_About ~about}
      }
   }

   gridplus button .toolbar -style Toolbutton -padding 0 -space 0 -takefocus 0 {
      {.new   :page_add     ?New   ~new}      \
      {.open  :folder_page  ?Open  ~openfile} \
      {.save  :disk         ?Save  ~save}     \
      |                                       \
      {.cut   :cut          ?Cut   ~gpcut}    \
      {.copy  :page_copy    ?Copy  ~gpcopy}   \
      {.paste :page_paste   ?Paste ~gppaste}  \
      |                                       \
      {.find  :page_find    ?Find   ~find}    \
      {&e .find-string 20 # ?Find   ~find} 
   }

   gridplus text .text -command modified -font {courier 8} -scroll xy -width 80 -height 25 -wrap none

   gridplus widget .statusbar -relief groove -stretch 0 {
      {"^File: " .file |>| "^Status: " .status}
   }

   gridplus layout .main -wtitle gpEdit {
        v
        .toolbar:w
      > .text:nsew
        .statusbar:ew
   }
   
   gpset {
      .statusbar,file   Untitled
      .statusbar,status New
   }

   gridplus pack .main -resize xy -minx 75 -miny 50
}

#==========Clear/initialise for new edit.
proc new {} {
   savemodified

   gpset {
      .text             {}
      .statusbar,file   Untitled
      .statusbar,status New
   }
}

#==========Open file for editing.
proc openfile {} {
   savemodified

   set filename [tk_getOpenFile -title "gpEdit: Open File"]

   if {$filename ne ""} {
      set file [open $filename r]
      gpset .text [read $file]
      close $file
      gpset .statusbar,file   $filename
      gpset .statusbar,status Opened
   }
}

#==========Save text to current file.
proc save {} {
   global {}

   if {$(.statusbar,file) != "Untitled"} {
      set file [open $(.statusbar,file) w]
      puts $file $(.text)
      close $file
   } else {
      saveas
   }

   gpset .statusbar,status Saved
}

#==========Save text to a new file.
proc saveas {} {
   global {}

   set filename [tk_getSaveFile -title "gpEdit: Save File"]

   if {$filename ne ""} {
      set file [open $filename w]
      puts $file $(.text)
      close $file
      gpset .statusbar,file   $filename
      gpset .statusbar,status Saved
   }
}

#==========Ask if file to be saved if it is modified.
proc savemodified {} {
   global {}

   if {$(.statusbar,status) eq "Modified"} {
      set action [tk_messageBox -message "Save the changes to [file tail $(.statusbar,file)]?" -icon warning -type yesnocancel -title gpEdit]

      switch -- $action {
         yes    {save}
         cancel {return -level 2}
      }
   }
}

#==========Find next occurance of specified string in text.
proc find {} {
   global {}

   gpfind .text $(.toolbar,find-string)
}

#==========Select all text.
proc selectall {} {
   .text.text tag add sel 1.0 end
}

#==========Display "About" dialog.
proc about {} {
   tk_messageBox -icon info -title "gpEdit: About" -message "gpEdit - Adrian Davis 2014" -type ok
}

#==========Set "Modified" status.
proc modified {} {
   global {}

   gpset .statusbar,status Modified
}

#==========Quit
proc quit {} {
   savemodified

   exit
}

#==========Start application.

gpoptions iconFile Icons

main

Comments

proc main {} {
Create a procedure called "main" to display the main/root window.
gridplus menu . {
   _File {
      {_New       ~new}
      {_Open      ~openfile}
      {_Save      ~save}
      {"Save _As" ~saveas}
      =
      {_Quit      ~exit}
   }
   _Edit { 
      {Cu_t       ~gpcut} 
      {_Copy      ~gpcopy} 
      {_Paste     ~gppaste} 
      {_Delete    ~gpclear}
      =
      {_Find     "~gpfind_dialog .text"}
      =
      {"_Select All" ~selectall}
  }
   _Help {
      {_About ~about}
   }
}

Create a menu called ".". Each menu option has a name and the name of the procedure to be called when the option is selected.

The underline characters are used to specify that the character following the underline will be displayed as underlined. The menu option can be selected by pressing the indicated character.

Pressing the "Alt" key will activate menu keyboard traversal.

The gpcut/gpcopy/gppaste/gpclear procudures are GRIDPLUS clipboard/edit commands. As the name of the item to which the command applies is not specified, the commands will operate on the widget which current has focus (if it is either an entry or a text widget). In this case either the ".text" text widget or the ".find,string" entry.

The gpfind_dialog procedure invokes the GRIDPLUS "Find" dialog.

gridplus button .toolbar -style Toolbutton -padding 0 -space 0 -takefocus 0 {
  {.new   :page_add     ?New   ~new}      \
  {.open  :folder_page  ?Open  ~openfile} \
  {.save  :disk         ?Save  ~save}     \
  |                                       \
  {.cut   :cut          ?Cut   ~gpcut}    \
  {.copy  :page_copy    ?Copy  ~gpcopy}   \
  {.paste :page_paste   ?Paste ~gppaste}  \
  |                                       \
  {.find  :page_find    ?Find   ~find}    \
  {&e .find-string 20 # ?Find   ~find} 
}

Creates a GRIDPLUS button grid called ".toolbar" using the "Toolbutton" style. Each button has an icon, explicitly defined name, associated pop-up/balloon help and a procedure to invoke.

For Example: "{.new :page_add ?New ~new}".

This button, named ".new", will have the ICONS (FamFamFam) image called "page_add", "New" as the pop-up/balloon help and will invoke a procedure called "new".

Next to the "Find" button is an entry grid called ".find-string" which is "20" characters wide. This content of this entry can be accessed as "$(.toolbar,find-string)". The "~find" option causes the "find" procedure to be invoked if the Enter/Return key is pressed while the entry has keyboard focus. The "#" widget option specifies the style to be used for the entry. In this case the name is null, which causes the default style to be used instead of "Toolbutton".

The gpcut/gpcopy/gppaste procudures are GRIDPLUS clipboard/edit commands. As the name of the item to which the command applies is not specified, the commands will operate on the widget which current has focus (if it is either an entry or a text widget). In this case either the ".text" text widget or the ".find,string" entry.

gridplus text .text -command modified -font {courier 8} -scroll xy -width 80 -height 25 -wrap none
Creates a text widget called ".text", which is "80" characters wide and "25" lines high. It has "xy" direction (vertical and horizontal) scrollbars. The font is set to "courier 8" and the word wrap is set to "none". The "-command modified" option specifies that the "modfied" procdure will be called when the content of the text widget is updated.

The GRIDPLUS text widget, which by default is editable, has a right-click pop-up Cut/Copy/Paste/Find menu.

gridplus grid .statusbar -relief groove {
   {"^File: " .file} {}
}

gridplus widget .statusbar -relief groove -stretch 0 {
   {"^File: " .file |>| "^Status: " .status}
}
Creates a GRIDPLUS grid called ".stausbar" with "groove" style relief.

An Embedded Grid is created with two columns. The colums are separated by a vertical bar ("|>|") which is attached to the right hand column. The first column has a text label "File". The caret character prefix causes the label to be displayed in bold text. To the right of the label is a another label called ".file" - the value of this label (.statusbar,file) can be set using the GRIDPLUS gpset command. The second column also has bold label text "Status:" and another label called ".status" - the value of this label (.statusbar,status) can be set using the GRIDPLUS gpset command.

The "-stretch 0" option specifies that the first column (column "0") in the ".statusbar" grid can stretch. As the Embedded Grid is created in the first column, this means that the contents of the Embedded Grid can stretch.

gridplus layout .main -wtitle gpEdit {
     v
     .toolbar:w
   > .text:nsew
     .statusbar:ew
}

gpset {
   .statusbar,file   Untitled
   .statusbar,status New
}

gridplus pack .main -resize xy -minx 75 -miny 50
Lays out the GRIDPLUS elements for the root/main window and displays the window contents. Using the "v" and ">" column/row weight and ":" sticky options the resize behaviour is set so that:-

  • The content of the ".toolbar" is attached to the left of the window - Expands in the "X" direction only.
  • The ".text" is attached to all sides of its containg cell - Expands in both "X" and "Y" directions.
  • The ".statusbar" is attached to both the left and right of its containing cell - Expands in the "X" direction only.

The ".statusbar,file" text is set to "Untitled" and ".statusbar,status" set to "New".

The gridplus pack must be used instead of the Tk pack command to pack a resizable window. The "-resize xy" option specifies that the window can be streched in both "x" (horizontal) and "y" (vertical) directions. The "-minx 75" option specifies that the minimum size to which the window can be resized in the "x" direction is to 75% of its initial size. The "-miny 50" option specifies that the minimum size to which the window can be resized in the "y" direction is to 50% of its initial size.


proc new {} {
   savemodified

   gpset {
      .text             {}
      .statusbar,file   Untitled
      .statusbar,status New
   }
}
Creates a procedure called "new". The "savemodified" procedure is called to check if there is unsaved data, and to give the user a chance to save the data. The contents of ".text" is set to to null, the statusbar filename to "Untitled" and the statusbar status to "New".


proc openfile {} {
   savemodified

   set filename [tk_getOpenFile -title "gpEdit: Open File"]

   if {$filename ne ""} {
      set file [open $filename r]
      gpset .text [read $file]
      close $file
      gpset .statusbar,file   $filename
      gpset .statusbar,status Opened
   }
}
Creates a procedure called "openfile". The "savemodified" procedure is called to check if there is unsaved data, and to give the user a chance to save the data. The "tk_getOpenFile" dialog is used to select a file for editing. If a file is selected, the file is opened, the value of ".text" is set to the contents of the file, the file is then closed. The value of the statusbar filename is set to the name of the file which has been selected and the statusbar status to "Opened".


proc save {} {
   global {}

   if {$(.statusbar,file) != "Untitled"} {
      set file [open $(.statusbar,file) w]
      puts $file $(.text)
      close $file
   } else {
      saveas
   }

   gpset .statusbar,status Saved
}
Creates a procedure called "save". This procedure saves the contents of ".text" to the file from which it was read.

If the data hasn't been been read from a file the statusbar filename will be "Untitled", in this case the "saveas" procedure will be called.

The statusbar status is set to "Saved".


proc saveas {} {
   global {}

   set filename [tk_getSaveFile -title "gpEdit: Save File"]

   if {$filename ne ""} {
      set file [open $filename w]
      puts $file $(.text)
      close $file
      gpset .statusbar,file   $filename
      gpset .statusbar,status Saved
   }
}
Creates a procedure called "saveas". This procedure uses the "tk_getSaveFile" dialog to set/select the name under which the contents of ".text" is to be saved. If a filename is set/selected, the file is opened, the value of ".text" is set to the contents of the file, the file is then closed. The value of the statusbar filename is set to the name of the file which has been set/selected and the statusbar status to "Saved".


proc savemodified {} {
   global {}

   if {$(.statusbar,status) eq "Modified"} {
      set action [tk_messageBox -message "Save the changes to [file tail $(.statusbar,file)]?" -icon warning -type yesnocancel -title gpEdit]

      switch -- $action {
         yes    {save}
         cancel {return -level 2}
      }
   }
}
Creates a procedure called "savemodified". This procedure checks to see if the statusbar status is "Modified". If it is a "tk_messageBox" is used to give the user the options to save the modified data, not save the modified data -or- cancel the operation.


proc find {} {
   global {}

   gpfind .text $(.find,string)
}
Creates a procedure called "find". This procedure is called when the toolbar "Find" button is pressed -or- when the enter key is press when the ".find,string" toolbar entry has focus.

The GRIDPLUS gpfind command is used to find the next occurance of ".find,string" in ".text".


proc selectall {} {
   .text.text tag add sel 1.0 end
}
Creates a procedure called ".selectall". This procedure selects all of the data in ".text".


proc about {} {
   tk_messageBox -icon info -title "gpEdit: About" -message "gpEdit - Adrian Davis 2014" -type ok
}
Creates a procedure called "about". This procedue displays the "About" dialog.


proc modified {} {
   global {}

   gpset .statusbar,status Modified
}
Creates a procedure called "modified". This procedue, invoked whenever ".text" is modified, sets the statusbar status to "Modified".


proc quit {} {
   savemodified

   exit
}
Creates a procedure called "quit". Before closing the application, the "savemodified" procedure is called to check if there is unsaved data, and to give the user a chance to save the data.


gpoptions iconFile Icons
The gpoptions command is used to Set the "*Gridplus.iconFile" database option to "Icons". For the purpose of this example "Icons" is a folder under the default "iconPath" which contains the FamFamFam PNG icon image files (See ICONS).


main
Runs the "main" procedure to create the gpEdit display.


Copyright © 2014 Adrian Davis.