[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

helpd - the help viewer main window



The helpd program is the program which receives
messages from the helplib library and converts these
messages to actions: loading various help pages and
related actions.

Currently the helpd program is written in tcl/tk for
speed of prototyping and ease of changing. This
may be rewritten in other languages without any
problem, as long as they can perform the following
functions:

1. Receive messages via a pipe.
2. Create a window which may be used by the display processes.
3. Send messages via a pipe to the display processes.


The main help daemon performs the following tasks:

1. Translates the messages from the incoming pipe to actions.
2. Keep track of view histories.
3. Match help files with the proper display processes.
4. Invoke display processes to display the corect help files.
5. Communicate with the display processes via pipes.
6. Provides navigation and main window related actions.

The current help daemon has several shortcomings which are
known, as well as many shortcomings which are not known.
In the near future improved versions will be written which
will then be posted.

Comments, as always, are welcome.
Note that filecomplete.tcl was not written by me, and is
not used with permission. I will track down the originator
and get permission, or write my own at a later date.

Ken Duck
twoducks@globalserve.net
#!/usr/bin/wish

# Help Daemon.
#
# This program provides three main functions:
#  1. Listens to the main help fifo pipe to receive messages from
#     programs using the help API.
#  2. Creates the main help frame and buttons, and handles navigation and
#     management of help files.
#  3. Manages display plugins and directs messages to control the
#     display.

########################################################################
# Initial settings and stuff
  source filecomplete.tcl

# Remove pipe if it already exists.
# If the pipe exists then problems can occur.

  if {[catch {set test [open help.fifo "RDONLY NONBLOCK"]}] == 0} \
  {
    close $test
    exec rm help.fifo
  }
# Navigation list stuff
  set navlist [list {}]
  set navptr 0

# set the fifo
  set fifo 0
  set plugin none


# Strings which are used in the frame

  set windowTitle "Help"
  set openButt "Open"
  set backButt "Back"
  set forebutt "Forward"
  set histButt "History"
  set splitButt "Split"
  set indexButt "Index"
  set contentButt "Contents"

#########################################################################
# Set the main help window gui

  wm title . $windowTitle

# Set the help space. This frame (.plugin) is only to be used to give the
# plugins a place to put their views of the documents.
  frame .plugin -width 600 -height 400 -container true
  pack .plugin -side bottom -fill both -expand true
  
# Get the ID of the window which plugin may get.
  set frameID [winfo id .plugin]
  

# Do menu
  menu .menu -tearoff 0
  menu .menu.file -tearoff 0
    .menu add cascade -label "File" -menu .menu.file -underline 0
    .menu.file add command -label "Open..." -command openFile
    .menu.file add command -label "Print Setup..." -command {error "this is just a demo: no action has been defined for the \"Print Setup...\" entry"}
    .menu.file add command -label "Print..." -command {error "this is just a demo: no action has been defined for the \"Print...\" entry"}
    .menu.file add separator
    .menu.file add command -label "Exit" -command exitTasks
    
  menu .menu.nav -tearoff 0
    .menu add cascade -label "Navigation" -menu .menu.nav -underline 0
    .menu.nav add command -label "Back" -command backFile
    .menu.nav add command -label "Forward" -command foreFile
    .menu.nav add separator
    .menu.nav add command -label "Search..." -command {error "this is just a demo: no action has been defined for the \"Search...\" entry"}
    .menu.nav add command -label "Contents..." -command {error "this is just a demo: no action has been defined for the \"Contents...\" entry"}
    .menu.nav add command -label "Index..." -command {error "this is just a demo: no action has been defined for the \"Index...\" entry"}

  menu .menu.conf -tearoff 0
    .menu add cascade -label "Configuration" -menu .menu.conf -underline 0
    .menu.conf add command -label "Plugins..." -command {error "this is just a demo: no action has been defined for the \"Plugins...\" entry"}
    .menu.conf add command -label "Appearance..." -command {error "this is just a demo: no action has been defined for the \"Appearance...\" entry"}

  
  . configure -menu .menu
  
# Set the quick navigation button bar.
  frame .butt
  pack .butt -side top -fill x
  
# Get the button images
    set leftImg [image create photo -file l_hand.gif]
    set rightImg [image create photo -file r_hand.gif]
    set searchImg [image create photo -file m_glass.gif]
    set openImg [image create photo -file folder3.gif]
    set indexImg [image create photo -file book.gif]
    set contentsImg [image create photo -file doc02.gif]
    set infoImg [image create photo -file info.gif]
  
# Set the buttons
    button .butt.open -image $openImg -command openFile
    button .butt.back -image $leftImg -command backFile
    button .butt.hist -text $histButt
    button .butt.search -image $searchImg
    button .butt.split -text $splitButt
    button .butt.index -image $indexImg
    button .butt.contents -image $contentsImg
    button .butt.fore -image $rightImg -command foreFile
    
    label .butt.info -image $infoImg
    
    pack .butt.open -side left -fill y
    pack .butt.back -side left -fill y
    pack .butt.fore -side left -fill y
#    pack .butt.hist -side left -fill y
#    pack .butt.split -side left -fill y
    pack .butt.search -side left -fill y
    pack .butt.index -side left -fill y
    pack .butt.contents -side left -fill y
    
    pack .butt.info -side right -fill y

####################################################################
# Actions caused by gui actions

# openFile allows the user to select a file from a file menu. This
#          file causes the appropriate plugin to be run and a message
#          sent down the pipeline.
  proc openFile {} \
  {
    global plugin frameID fifo
    global navlist navptr
 
    set filename [fileselect "Open Help File"]
    if {[string compare $filename ""] == 0} {return}
 
    #Open a new help file
    if {[string compare $plugin none] == 0} \
    {
      # Start the plugin
      exec wish -f testPlugin -use $frameID \&
      
      puts stdout [file type plugin.fifo]
      
      # Start the fifo
     set fifo [open "plugin.fifo" w]
     set plugin html
    }

    incr navptr
    if {$navptr >= [llength $navlist]} \
    {
      lappend navlist $filename
    } \
    else \
    {
      set navlist [lreplace $navlist $navptr end $filename]
    }
    

    writeFifo "open $filename"
  }
  
# backFile causes the main plugin to set the plugin for the previous
#          document and send the previous filename down the pipeline.
  proc backFile {} \
  {
    global plugin frameID fifo
    global navlist navptr
    if {$navptr > 1} \
    {
      incr navptr -1
    }
    
    #Open a new help file
    if {[string compare $plugin none] == 0} \
    {
      # Start the plugin
      exec wish -f testPlugin -use $frameID \&
      
      puts stdout [file type plugin.fifo]
      
      # Start the fifo
     set fifo [open "plugin.fifo" w]
     set plugin html
    }

    writeFifo "open [lindex $navlist $navptr]"
  }

# foreFile causes the main plugin to set the plugin for the next
#          document and send the next filename down the pipeline.
  proc foreFile {} \
  {
    global plugin frameID fifo
    global navlist navptr
    if {$navptr < [expr [llength $navlist] - 1]} \
    {
      incr navptr
    }
    
    #Open a new help file
    if {[string compare $plugin none] == 0} \
    {
      # Start the plugin
      exec wish -f testPlugin -use $frameID \&
      
      puts stdout [file type plugin.fifo]
      
      # Start the fifo
     set fifo [open "plugin.fifo" w]
     set plugin html
    }

    writeFifo "open [lindex $navlist $navptr]"
  }

# writeFifo cuses a message to be sent down the plugin pipeline
  proc writeFifo {cmd} \
  {
    global fifo
    # open the file
    puts $fifo $cmd
    close $fifo
    set fifo [open "plugin.fifo" w]
  }

# reader is involked when the help pipeline is written to. A pipe reader
#        is opened on the pipe and the message is read and handled.
  proc reader {pipe} \
  {
    global plugin frameID fifo
    global navptr navlist
    
    if [eof $pipe] \
    {
      puts stderr "eof!"
      catch {close $pipe}
      set pipe [open help.fifo "RDONLY NONBLOCK"]
      fileevent $pipe readable [list reader $pipe]
      return
    } \
    else \
    {
      set actualpipe [open help.fifo r]
    }
    gets $actualpipe line
    if {[regexp ^open\ (.*) $line matching filename] != 0} \
    {
      puts stdout "open $filename"
      if {[string compare $filename ""] == 0} {return}
 
      #Open a new help file
      if {[string compare $plugin none] == 0} \
      {
        # Start the plugin
        exec wish -f testPlugin -use $frameID \&
      
        puts stdout [file type plugin.fifo]
      
        # Start the fifo
       set fifo [open "plugin.fifo" w]
       set plugin html
      }
      incr navptr
      if {$navptr >= [llength $navlist]} \
      {
        lappend navlist $filename
      } \
      else \
      {
        set navlist [lreplace $navlist $navptr end $filename]
      }

      writeFifo "open $filename"
    }
    close $actualpipe
      catch {close $pipe}
      set pipe [open help.fifo "RDONLY NONBLOCK"]
      fileevent $pipe readable [list reader $pipe]
    puts stdout "closed pipe"
  }

# The following tasks set up the help pipe so that actions may be sent
# to the main help window.
  if {[catch {set pipe [open help.fifo "RDONLY NONBLOCK"]}] != 0} \
  {
    exec mkfifo help.fifo
  } \
  else \
  {
    close $pipe
  }
  set pipe [open help.fifo "RDONLY NONBLOCK"]
  fileevent $pipe readable [list reader $pipe]

# exitTasks are the tasks that need to be performed to cleanly close the
#           program.
  proc exitTasks {} \
  {
    global plugin
    if {[string compare $plugin none] != 0} \
    {
      writeFifo close
    }
    exec rm help.fifo
    exit
  }
  
# Assume that since the program is started something is getting sent to
# it. This will prevent the missing of an event between the creation of
# the pipe and the callback handler.

# If there is a command line argument, this is a file name. Show it.
# Elsewise start watching the pipe.
  puts stderr "$argc $argv"
  if {$argc > 0} \
  {
    #Open a new help file
    if {[string compare $plugin none] == 0} \
    {
      # Start the plugin
      exec wish -f testPlugin -use $frameID \&
      
      puts stdout [file type plugin.fifo]
      
      # Start the fifo
      set fifo [open "plugin.fifo" w]
      set plugin html
    }
    incr navptr
    if {$navptr >= [llength $navlist]} \
    {
      lappend navlist $argv
    } \
    else \
    {
      set navlist [lreplace $navlist $navptr end $filename]
    }

    writeFifo "open $argv"
    set pipe [open help.fifo "RDONLY NONBLOCK"]
    fileevent $pipe readable [list reader $pipe]
  } \
  else \
  {
    reader $pipe
  }

filecomplete.tcl