Index: tolbase/lib/markupviewer/markupviewer.tcl
===================================================================
--- tolbase/lib/markupviewer/markupviewer.tcl	(revision 4721b731b158fb7140e9979c5e50db14ceea63de)
+++ tolbase/lib/markupviewer/markupviewer.tcl	(revision 4721b731b158fb7140e9979c5e50db14ceea63de)
@@ -0,0 +1,464 @@
+#-----------------------------------------------------------------------
+# TITLE:
+#       markupviewer.tcl
+#
+# AUTHOR:
+#       inspired in Will Duquette
+#
+# DESCRIPTION:
+#       A viewer for browsing Notebook pages. Simplified version of
+#       pageviewer.tcl
+#
+# LICENSE:
+#
+#-----------------------------------------------------------------------
+
+package require snit
+package require renderpane
+
+package require autoscroll
+
+snit::widget markupviewer {
+    # A command to use for outputting messages.  The message will be
+    # appended.
+    option -messagecommand {}
+  
+    #-------------------------------------------------------------------
+    # Other options
+
+    # A command that's called when the displayed page changes. The
+    # command gets one argument, the page name.
+    option -pagecommand {}
+
+    # A command that's called when we try to display a page that
+    # doesn't exist.
+    option -unknowncommand {}
+
+    # The notebook database
+    option -db -default {} -readonly 1
+
+    # control if the title is rendered on the renderpane
+    option -showtitle -default 1 
+
+    #-------------------------------------------------------------------
+    # Components
+
+    variable db
+
+    # The renderpane is the component that actually displays the
+    # rendered page.
+    variable rp
+
+    #--------------------------------------------------------------------
+    # Other variables
+
+    # The history list is a list of {PageName PagePos} pairs.
+    variable history {}
+
+    # The histPos is the index of the currently displayed page in the
+    # history list.  It's initially -1 because there's nothing in the
+    # list.
+    variable histPos -1
+
+    # A list of recently displayed pages.  Each page appears only once,
+    # regardless of how often (and where) it appears in the history list.
+    # There will be ten pages listed, maximum; no more than one of them 
+    # are guaranteed to still exist.
+    variable recentPages {}
+
+    #--------------------------------------------------------------------
+    # Constructor
+
+    constructor {args} {
+        # As yet we have no option delegation, so go ahead and get the
+        # args
+        $self configurelist $args
+
+        if { $options(-db) eq "" } {
+          error "Invalid database option: '$options(-db)'"
+        }
+        set db $options(-db)
+
+        # Create the renderpane.
+        install rp using renderpane $win.rp \
+            -yscrollcommand "$win.scroll set" \
+            -querycommand      [mymethod pageexists] \
+            -titlecommand      [mymethod TitleHandler] \
+            -linkcommand       [mymethod LinkHandler] \
+            -buttoncommand     [mymethod ButtonHandler] \
+            -macroerrorcommand [mymethod MacroErrorHandler] \
+            -messagecommand    $options(-messagecommand)
+
+        scrollbar $win.scroll \
+            -command "$win.rp yview"
+        ::autoscroll::autoscroll $win.scroll
+
+        # grid them in.
+        
+        if { 0 } {
+          pack $win.scroll  -side right -fill y    -expand false
+          pack $rp          -side top   -fill both -expand true
+        }
+
+        grid $rp -row 0 -column 0 -sticky "snew"
+        grid $win.scroll -row 0 -column 1 -sticky "ns"
+        grid rowconfigure $win 0 -weight 1
+        grid columnconfigure $win 0 -weight 1
+
+        #---------------------------------------------------------------
+        # User Interactions
+
+        # Add the pageviewer window itself to the renderpane's bindtags,
+        # so that keys bound to the pageviewer will take effect when the
+        # renderpane has focus
+        set tags [bindtags $rp]
+        lappend tags $win
+        bindtags $rp $tags
+
+        # Go to the previous page when <BackSpace> is pressed.
+        bind $rp <BackSpace> [list $self backpage]
+
+        # User Menu.
+        if { 0 } {
+          menu $rp.user -tearoff no -postcommand [mymethod PostUserMenu]
+          
+          $rp.user add command -label "User Menu"
+          $rp.user add separator
+          $rp.user add command -label "Dummy"
+          
+          bind $rp <Control-1> [list tk_popup $rp.user %X %Y 2]
+          bind $rp <3>         [list tk_popup $rp.user %X %Y 2]
+          
+          # Recent Pages Menu.
+          menu $rp.recent -tearoff no -postcommand [mymethod PostRecent]
+          
+          $rp.recent add command -label "Recent Pages"
+          $rp.recent add separator
+          $rp.recent add command -label "Dummy"
+          
+          bind $rp <Shift-1> [list tk_popup $rp.recent %X %Y 2]
+        }
+        #---------------------------------------------------------------
+        # Prepare to receive preferences events; unregister on 
+        # destroy.
+        prefs register $selfns [mymethod UsePreferences]
+    }
+
+    destructor {
+        catch {prefs unregister $selfns}
+    }
+
+    method UsePreferences {} {
+      # Update the display when the preferences change.
+      $self showpage
+    }
+    #-------------------------------------------------------------------
+    # Public Methods
+
+    delegate method searchfor to rp
+
+    delegate method pagetime     to db
+    delegate method pagename     to db      as name
+
+    method pageexists { name } {
+      if { [ $db exists $name ] } {
+        return 1
+      } else {
+        return [ regexp "(http|https)://" $name ]
+      }
+    }
+
+    # Returns the expanded text of the page, which must exist.
+    method pageexpand {name} {
+      try {
+        set result [$db expand [$db get $name]]
+      }
+      return $result
+    }
+
+    # Returns the name of the currently displayed page, or "" if none.
+    # Note that during a deletion or page renaming the named page
+    # is not guaranteed to exist.
+    method current {} {
+      return [lindex $history $histPos 0]
+    }
+
+    # Tells the pageviewer to take the keyboard focus.
+    # If -friendly, we take the focus only if our toplevel already
+    # has it (e.g., we take the focus back from the search box).
+    method focus {{option {}}} {
+      set focusWin [focus]
+      
+      if {$focusWin eq ""} {
+        return
+      }
+      
+      if {$option ne "-friendly" || 
+          [winfo toplevel $win] eq [winfo toplevel $focusWin]} {
+        focus $rp
+      }
+    }
+    
+    # Shows the named page.  The -unknowncommand is called if the
+    # page doesn't exist.
+    method showpage {{name ""}} {
+        # FIRST, remember the position for the page we're showing now.
+        if {$histPos >= 0} {
+            lset history $histPos 1 [$self GetPos]
+        }
+
+        # NEXT, default to the current page.
+        if {$name eq ""} {
+            set name [$self current]
+
+            if {$name eq ""} {
+                error "no current page."
+            }
+        }
+
+        # NEXT, if the page doesn't exist, call the -unknowncommand.
+        if {![$self pageexists $name]} {
+            if {$options(-unknowncommand) ne {}} {
+                set command $options(-unknowncommand)
+                lappend command $name
+                uplevel \#0 $command
+            } else {
+                return -code error -errorcode USER "No such page: '$name'"
+            }
+
+            return
+        }
+
+        # NEXT, save the new page in the history list.  When we show
+        # a new page, any pages forward of our position go away.
+        set history [lrange $history 0 $histPos]
+        lappend history [list [$self pagename $name] 0]
+        incr histPos
+
+        # NEXT, display it.
+        $self DisplayPage
+    }
+    # Steps back one page, if possible
+    method backpage {} {
+        # FIRST, remember the position for the page we're showing now.
+        if {$histPos >= 0} {
+            lset history $histPos 1 [$self GetPos]
+        }
+
+        # NEXT, Look back through the history list for a page that exists,
+        # and is also different than the current page.
+        set current [lindex $history $histPos 0]
+        set pos [expr {$histPos - 1}]
+
+        while {$pos >= 0} {
+            set candidate [lindex $history $pos 0]
+
+            if {[$self pageexists $candidate]} {
+
+                if {![string equal -nocase $candidate $current]} {
+                    break
+                }
+            }
+
+            incr pos -1
+        }
+
+        if {$pos < 0} {
+            bell
+            return
+        }
+
+        set histPos $pos
+
+        $self DisplayPage
+    }
+
+    # Steps forward one page, if possible
+    method forwardpage {} {
+        # FIRST, remember the position for the page we're showing now.
+        if {$histPos >= 0} {
+            lset history $histPos 1 [$self GetPos]
+        }
+
+        # NEXT, Look forward through the history list for a page that exists,
+        # and is also different than the current page.
+        set current [lindex $history $histPos 0]
+        set pos [expr {$histPos + 1}]
+        set len [llength $history]
+
+        while {$pos < $len} {
+            set candidate [lindex $history $pos 0]
+
+            if {[$self pageexists $candidate]} {
+
+                if {![string equal -nocase $candidate $current]} {
+                    break
+                }
+            }
+
+            incr pos
+        }
+
+        if {$pos >= $len} {
+            bell
+            return
+        }
+
+        set histPos $pos
+
+        $self DisplayPage
+    }
+
+    # Shows the page from the beginning of the Recent Pages list, if it exists.
+    method cyclerecent {} {
+        foreach id $recentPages {
+            if {[$self pageexists $id]} {
+                $self showpage $id
+                break
+            }
+        }
+    }
+
+    #-------------------------------------------------------------------
+    # Private Methods
+
+    # Display the current page
+    method DisplayPage { } {
+        # FIRST, Get the current page specs
+        set name [$self pagename [lindex $history $histPos 0]]
+        set ypos [lindex $history $histPos 1]
+
+        # NEXT, add the name to the list of recent pages
+        $self SaveRecentPage $name
+
+        # NEXT, Expand the requested page and display it.
+        set pagetext [$self pageexpand $name]
+
+        if { $options(-showtitle) } {
+          $rp render $name $pagetext
+        } else {
+          $rp render "" $pagetext
+        }
+        
+        if { 0 } {
+          $rp searchfor [$self searchtext]
+        }
+        # NEXT, give the renderpane the focus so that we don't
+        # need to click in it.
+        $self focus -friendly
+
+        $self setpos $ypos
+
+        # NEXT, notify those concerned.
+        if {$options(-pagecommand) ne {}} {
+            set command $options(-pagecommand)
+            lappend command $name
+            uplevel \#0 $command
+        }
+    }
+
+    # Save the specified page to the recent pages list, removing
+    # any previous entry with the same name.  Names are stored
+    # in lower case.  No more than ten names are stored.
+    method SaveRecentPage {name} {
+        set name [string tolower $name]
+
+        set ndx [lsearch -exact $recentPages $name]
+
+        if {$ndx != -1} {
+            set recentPages [lreplace $recentPages $ndx $ndx]
+        }
+
+        lappend recentPages $name
+        
+        # Cut down back to ten, if need be
+        if {[llength $recentPages] == 11} {
+            set recentPages [lreplace $recentPages 0 0]
+        }
+    }
+    
+
+    # Get the y-position of the text at the top of the window.
+    method GetPos {} {
+        lindex [$rp yview] 0
+    }
+
+    # Set the y-position of the text at the top of the window.
+    method setpos  {ypos} {
+        $rp yview moveto $ypos
+    }
+
+    # Called when the user clicks on the title text.
+    method TitleHandler {title} {
+        $self showsearch $title
+    }
+
+    # Called when the user clicks on a link; shows the linked page.
+    method LinkHandler {linkName} {
+      if { [ regexp "(http|https)://" $linkName ] } {
+        LaunchBrowser $linkName
+      } else {
+        $self showpage $linkName
+      }
+    }
+
+    # Called when the user clicks on a magic button; calls the button's script
+    method ButtonHandler {script} {
+        $self evaluser $script
+    }
+
+    # Finds the macro error text and displays the error message.
+    method MacroErrorHandler {macro} {
+        $self evaluser $macro
+        # TBD: If macros are disabled, then clicking on this won't 
+        # display an error.  We need to handle this somehow.
+    }
+
+    # procs
+
+    proc LaunchBrowser { url } {
+      global tcl_platform
+      
+      # It *is* generally a mistake to switch on $tcl_platform(os), particularly
+      # in comparison to $tcl_platform(platform).  For now, let's just regard it
+      # as a stylistic variation subject to debate.
+      switch $tcl_platform(os) {
+        Darwin {
+          set command [list open $url]
+        }
+        HP-UX -
+        Linux  -
+        SunOS {
+          foreach executable {firefox mozilla netscape iexplorer opera lynx
+            w3m links epiphany galeon konqueror mosaic amaya
+            browsex elinks} {
+            set executable [auto_execok $executable]
+            if [string length $executable] {
+              # Do you want to mess with -remote?  How about other browsers?
+              # Frank Darby says the '&' leads on his Ubuntu to attempts to
+              # open 'www.&.com', and so on.
+              set command [list $executable $url &]
+              set command [list $executable $url]
+              break
+            }
+          }
+        }
+        {Windows 95} -
+        {Windows NT} {
+          set command "[auto_execok start] {} [list $url]"
+        }
+      }
+      if [info exists command] {
+        # Replace {*}$command by eval "$command" if you want < tcl 8.5 compatibility ([RA])
+        # Added the '&' to launch the browser as background process. [Duoas]
+        if [catch {eval exec $command &} err] {
+          tk_messageBox -icon error -message "error '$err' with '$command'"
+        }
+      } else {
+        tk_messageBox -icon error -message \
+            "Please tell CL that ($tcl_platform(os), $tcl_platform(platform)) is not yet ready for browsing."
+      }
+    }
+}
+
+package provide markupviewer 1.0
Index: tolbase/lib/markupviewer/pkgIndex.tcl
===================================================================
--- tolbase/lib/markupviewer/pkgIndex.tcl	(revision 4721b731b158fb7140e9979c5e50db14ceea63de)
+++ tolbase/lib/markupviewer/pkgIndex.tcl	(revision 4721b731b158fb7140e9979c5e50db14ceea63de)
@@ -0,0 +1,11 @@
+# Tcl package index file, version 1.1
+# This file is generated by the "pkg_mkIndex" command
+# and sourced either when an application starts up or
+# by a "package unknown" script.  It invokes the
+# "package ifneeded" command to set up package-related
+# information so that packages will be loaded automatically
+# in response to "package require" commands.  When this
+# script is sourced, the variable $dir must contain the
+# full path name of this file's directory.
+
+package ifneeded markupviewer 1.0 [list source [file join $dir markupviewer.tcl]]
Index: tolbase/lib/nbdbmanager/nbdbmanager.tcl
===================================================================
--- tolbase/lib/nbdbmanager/nbdbmanager.tcl	(revision 4721b731b158fb7140e9979c5e50db14ceea63de)
+++ tolbase/lib/nbdbmanager/nbdbmanager.tcl	(revision 4721b731b158fb7140e9979c5e50db14ceea63de)
@@ -0,0 +1,134 @@
+package require BWidget
+package require snit
+package require mimetex
+package require notebookdb
+package require trycatch
+
+if { [ info commands "try" ] eq "" } {
+  namespace import ::trycatch::*
+}
+
+
+snit::type nbdbmanagerType {
+
+  #-------------------------------------------------------------------
+  # Instance Variables
+  
+  # This is an array, indexed by normalized notebook filename.
+  # The elements are lists: db <notebookdb> ref <refcount>
+  variable registry
+
+  method create { name } {
+    # FIRST, put the file name into canonical form as an index key.
+    set canonicalName [file normalize $name]
+    
+    # NEXT, do we have a notebookdb for this name?
+    if {[info exists registry($canonicalName)]} {
+      
+      # We do.  Increment its reference count.
+      array set data $registry($canonicalName)
+      incr data(ref)
+      set registry($canonicalName) [array get data]
+      
+      # Return the object.
+      return $data(db)
+    }
+    # NEXT, try to open a file with this name.  It might fail with
+    # an error, which we won't bother catching; we'd just need to
+    # rethrow it.
+    set data(db) [ ::notebookdb::notebookdb create %AUTO% -dbfile $name ]
+    
+    # NEXT, it succeeded.  Add it to the registry with a reference
+    # count of 1.
+    set data(ref) 1
+    set registry($canonicalName) [array get data]
+    
+    # NEXT, define the Notebook Commands and the required pages
+    $self InitializeCommands $data(db)
+    
+    # FINALLY, return the new notebookdb.
+    return $data(db) 
+  }
+
+  method InitializeCommands { db } {
+    # FIRST, define a private namespace for Notebook's use
+    $db eval "namespace eval ::__notebook:: { }"
+    
+    # FIRST, alias "kernel" commands defined elsewhere in the
+    # application.
+    $db alias parse             ::markupparser::parse
+    $db alias image             image
+    $db alias GetImage          Bitmap::get
+    $db alias mimetexRender     mimetex::RenderImage
+    $db eval {
+      ::db::PageExpander evalcmd escapemime
+      ::db::PageExpander errmode error
+      
+      proc escapemime { macro } {
+        #puts "escapeeval = '$macro'"
+        if { [ string range [ string trimleft $macro ] 0 6 ] eq "mimetex" } {
+          set latex [ string range $macro 7 end ]
+          return [ mimetex $latex ]
+        } else {
+          eval $macro
+        }
+      }
+      
+      proc img { imageORfile } {
+        if { [ lsearch [ image names ]  $imageORfile ] == -1 } {
+          set img [ GetImage $imageORfile ]
+        } else {
+          set img $imageORfile
+        }
+        return "\[!image $img !\]"
+      }
+      
+      proc mimetex { latex } {
+        set img [ mimetexRender $latex ] 
+        return "\[!image $img !\]"
+      }
+      
+      array set urlConf {
+        tol,root https://www.tol-project.org
+        tol,wiki https://www.tol-project.org/wiki
+        tol,milestone https://www.tol-project.org/milestone
+        tol,ticket https://www.tol-project.org/ticket
+        mms,root https://trac.localbayes.es/mms
+        mms,wiki https://trac.localbayes.es/mms/wiki
+        mms,milestone https://www.tol-project.org/mms/milestone
+        mms,ticket https://www.tol-project.org/mms/ticket
+      }
+      
+      # [@url mms howto/5 "HOWTO 5" wiki @]
+      # [@url mms howto/5 "HOWTO 5" @]
+      # [@url mms howto/5 @]
+      proc url { where path args } {
+        foreach { label section } {"" ""} break
+        foreach { label section } $args break
+        if { $section eq "" } {
+          set section "wiki"
+        }
+        set urlroot $::urlConf(${where},root)
+        set url "$::urlConf(${where},root)/$section/$path"
+        if { $label eq "" } {
+          return "\[${url}\]"
+        } else {
+          return "\[$label | ${url}\]"
+        }
+      }
+
+      # [@mms howto/5 "HOWTO 5" wiki @]
+      # [@mms howto/5 "HOWTO 5" @]
+      # [@mms howto/5 @]
+      proc mms { path args } {
+        return [ eval url mms $path $args ]
+      }
+
+      proc tol { path args } {
+        return [ eval url tol $path $args ]
+      }
+    }
+  }
+}
+
+package provide nbdbmanager 1.0
Index: tolbase/lib/nbdbmanager/pkgIndex.tcl
===================================================================
--- tolbase/lib/nbdbmanager/pkgIndex.tcl	(revision 4721b731b158fb7140e9979c5e50db14ceea63de)
+++ tolbase/lib/nbdbmanager/pkgIndex.tcl	(revision 4721b731b158fb7140e9979c5e50db14ceea63de)
@@ -0,0 +1,11 @@
+# Tcl package index file, version 1.1
+# This file is generated by the "pkg_mkIndex" command
+# and sourced either when an application starts up or
+# by a "package unknown" script.  It invokes the
+# "package ifneeded" command to set up package-related
+# information so that packages will be loaded automatically
+# in response to "package require" commands.  When this
+# script is sourced, the variable $dir must contain the
+# full path name of this file's directory.
+
+package ifneeded nbdbmanager 1.0 [list source [file join $dir nbdbmanager.tcl]]
