A pocket Wiki

Richard Suchenwirth 2008-06-24 - The following is a rather small program, which may not fulfil all expectations people might have from a Wiki. It is

  • a stand-alone "app", designed for use on PDAs for managing structured notes
  • presenting a set of pages which are connected by clickable links (blue and underlined)
  • editable directly in the rendered page (no separate edit mode, text widget)

The markup language consists only of [brackets] around link names. Pages are created if they don't exist, when a link to them is clicked. Each page is rendered with two special links, "Back" and "Index" (leading to an alphabetic page list, recreated every call time).

WikiDbImage pwiki.jpg

When a page is left, it is saved to the backing store (the array p). When the app exits, the contents of the backing store are saved to the file it was started with (or, if no name was given, pwiki.txt). No warranty as usual, but enjoy :^)

 package require Tk
 set thisdir [info script]/..
 proc main argv {
    global p history
    if {[llength $argv]==0} {set ::argv [list $::thisdir/pwiki.txt]}
    source [lindex $::argv 0]
    pack [scrollbar .y -command ".t yview"] -fill y -side right
    pack [text .t -wrap word -yscrollcommand ".y set" -font {Helvetica 10}] \
        -fill both -expand 1 -side left
    .t tag configure head -font {Helvetica 16}
    .t tag configure link -foreground blue -underline 1
    .t tag bind link <1> "click %W %x %y"
    bind .t <F5> {render %W [lindex $history end]}
    set history ""
    render .t ""
    bind . <Destroy> {save [lindex $::argv 0]}
    update idletasks
    catch {bind . <ConfigureRequest> {::etcl::autofit %W}}
 proc render {w page} {
    if {$page eq "Back"} {set page [do_back $w]}
    if {$page eq "Index"} {do_index $w}
    global p history
    if {[lindex $history end] ne $page} {lappend history $page}
    set title [lindex [split [$w get 1.0 1.end] \t] 0]
    if {$title ne ""} {
        set p($title) [string trim [$w get 2.0 end] \n] ;# save previous page
    $w delete 1.0 end
    if ![info exists p($page)] {set p($page) ""}
    $w insert end $page head \t "" Back link " - " "" Index link \n\n
    foreach line [split $p($page) \n] {
        foreach {nonlink link} [split $line {[]}] {
            $w insert end $nonlink
            if {$link ne ""} {$w insert end \[ "" $link link \] ""}
        $w insert end \n
 proc click {w x y} {
    set range [$w tag prevrange link [$w index @$x,$y]]
    if [llength $range] {render $w [eval $w get $range]}
 proc do_back w {
    global history
    set history [lrange $history 0 end-1]
    lindex $history end
 proc do_index w {
    global p
    set p(Index) ""
    foreach i [lsort -dict [array names p]] {
        if {$i ne "Index"} {append p(Index) \[$i\]\n}
 proc save filename {
    global p
    set f [open $filename w]
    foreach i [lsort -dict [array names p]] {
        puts $f [list set p($i) $p($i)]
    close $f
 main $argv

MHo: An error message occurs if pwiki.txt isn't there.

LV: An error message occurs if the file passed in as an argument isn't there as well. Also, if the file being source'd in has some sort of error in it (say someone tried editing the file directly, etc.), an error will be raised.

RS Hm.. and the error message says "no such file or directory"? Isn't that helpful enough? :^) For hand-edited files, the user who did it is of course the sole responsible.

LV I wonder how much code would be needed to add support of color images. It might be interesting to be able to build a wiki around one's photos.

RS This much - 6 lines to replace the one $link ne "" line above:

            if {$link ne ""} {
                if [file exists $::thisdir/$link] {
                    set im [image create photo -file $::thisdir/$link]
                    $w image create end -image $im
                } else {$w insert end \[ "" $link link \] ""}

This assumes the images are all in the same directory as pwiki.tcl and pwiki.txt. However, when saving back pages, the image pathnames get lost... so we'll need some more thinking here...#

WJG (28/0607) Richard, I couldn't get the pages to save. Have I missed out a step? Yet this looks like another RS testpiece. Would you have any objection if I later adapt the code to create a Gnocl version? - RS: First, all code I post is free as air, so feel free to do anything with it. For the saving problem: I did of course test before I wikified, but forgot to mention some test conditions (which come from imperfect design):

  • The page with title "" is not saved after modification
  • For testing, one needs an initial file which at least introduces one link to another page:
 set p() {[About]}

This will prepare a page titled "About", from where you can start creating pages. Not brilliant design.. will think it over.