#! ../mofe --f
#! /bin/sh
# Flip the first two lines above to search for mofe on the searchpath \
  exec mofe --f $0 ${1+"$@"} 
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# This a more elaborate version of the postscript viewer using
# the XmNoteBook widget if available. 
#
# Note: this script needs perl to scan the postscript file!
#
# Authors: Peter Sylvester + Gustaf Neumann
scriptNeeds Ghostview {
  This Script requires a version of mofe compiled with 
  support for the Ghostview widget
}
scriptNeeds Object {
  This Script requires a version of mofe compiled with 
  support for the OTcl - MIT Object Tcl (Version 0.94 or better).
}

# set useList to 0 if you have Motif 2.0, but you prefer the list
# widget display for the page numbers...; otherwise set useList to 1
set useList 1

fallbackResources topLevel \
    *mp1.Next-Page.accelerator          <Key>Next  \
    *mp1.Next-Page.acceleratorText      PageDown   \
    *mp1.Next-Page.mnemonic             N          \
    *mp1.Previous-Page.accelerator      <Key>Prior \
    *mp1.Previous-Page.acceleratorText  PageUp     \
    *mp1.Previous-Page.mnemonic         P          \
    *mp1.Quit.accelerator               Ctrl<Key>c \
    *mp1.Quit.acceleratorText           Ctrl-C     \
    *mp1.Quit.mnemonic                  Q          \
    *mp1.Load.mnemonic                  L          \
    *mp1.Load.accelerator               <Key>l     \
    *mp1.Load.acceleratorText           l          \
    *mp1.Reload.mnemonic                R          \
    *mp1.Reload.accelerator             <Key>r     \
    *mp1.Reload.acceleratorText         r          \
    *mp3.Zoom-In.accelerator            <Key>i     \
    *mp3.Zoom-In.acceleratorText        i          \
    *mp3.Zoom-Out.accelerator           <Key>o     \
    *mp3.Zoom-Out.acceleratorText       o          \
    *mp3.Center-Page.accelerator        <Key>c     \
    *mp3.Center-Page.acceleratorText    c          \
    *background                         gainsboro  \
    *g.background                       gray97     \
    *g.foreground                       black      \
    *foreground                         black

mergeResources topLevel \
    *mp2*activateCallback "sV g orientation %w; gsv refreshCurrentPage g" \
    *pages*browseSelectionCallback {gsv nextPage g [expr %p-1]}

if {$ARGC == 2} { 
  set default_ps_doc [lindex $ARGV 1] 
}

XmMainWindow main topLevel  width 725 height 950 

  XmMenuBar menu_bar main
  sV main menuBar menu_bar
    XmPulldownMenu mp1 menu_bar unmanaged
      XmPushButton Load      mp1 activateCallback "manageChild fs"
      XmPushButton Reload    mp1 activateCallback {gsv reload} \
                                 sensitive 0
      XmPushButton Previous-Page mp1 activateCallback "gsv nextPage g -1"
      XmPushButton Next-Page mp1 activateCallback "gsv nextPage g +1"
      XmPushButton Quit      mp1 activateCallback "gsv stop"
    XmCascadeButton File menu_bar subMenuId mp1  mnemonic F

    XmPulldownMenu mp2 menu_bar unmanaged
      XmPushButton portrait    mp2
      XmPushButton landscape   mp2
      XmPushButton upside-down mp2
      XmPushButton seascape    mp2
    XmCascadeButton Orientation menu_bar subMenuId mp2 mnemonic O

    XmPulldownMenu mp3 menu_bar unmanaged
      XmPushButton Zoom-In     mp3 activateCallback "gsv zoom g 1.2"
      XmPushButton Zoom-Out    mp3 activateCallback "gsv zoom g 1/1.2"
      XmPushButton Center-Page mp3 activateCallback "gsv centerPage"
    XmCascadeButton Zoom menu_bar subMenuId mp3  mnemonic Z

    XmPulldownMenu mph menu_bar unmanaged
      XmPushButton Help    mph activateCallback "manageChild helpbox"
      XmPushButton Version mph activateCallback "manageChild versionbox"
    XmCascadeButton Help menu_bar subMenuId mph  mnemonic H


Object gsv
gsv set filehandle ""
gsv set timeout ""

#### we want to use this script with Motif 1.* AND Motif 2.0
#### there comes the code depending on the available widgets
if {$useList || [string match "" [info command XmNotebook]]} {
  # we have no XmNoteBook widget
  gsv proc setPageNumber {n} {
    XmListSelectPos pages $n 0
  }
  gsv proc setCurrentPage {n} {
    $self instvar currentpage
    $self setRawPage g $currentpage
  }
  gsv proc scale {w factor} {
    sV $w \
	xdpi [expr [gV $w xdpi]*$factor] \
	ydpi [expr [gV $w ydpi]*$factor] 
  }
  gsv proc create {} {
    XmForm form main width 750 height 900 
    XmScrolledList pages form \
	bottomAttachment ATTACH_FORM topAttachment ATTACH_FORM \
	width 65 resizable false
    XmScrolledWindow scrolled form \
	scrollingPolicy AUTOMATIC \
	bottomAttachment ATTACH_FORM topAttachment ATTACH_FORM \
	leftAttachment ATTACH_WIDGET leftWidget pages \
	rightAttachment ATTACH_FORM
    Ghostview g scrolled
  }
  gsv proc setPageLabels {} {
    $self instvar pagelabels maxpage
    sV pages items $pagelabels itemCount $maxpage
  }
  gsv proc setFocus {} {
    XmProcessTraversal scrolled TRAVERSE_CURRENT
  }
} else {
  # we have a XmNoteBook widget
  gsv proc setPageNumber {n} {
    sV g pageNumber $n
  }
  gsv proc setCurrentPage {n} {
    sV gnotebook currentPageNumber $n
  }
  gsv proc scale {w factor} {
    sV gnotebook \
	width [expr int([gV gnotebook width]*$factor)] \
	height [expr int([gV gnotebook height]*$factor)] 
  }
  gsv proc create {} {
    XmScrolledWindow scrolled main scrollingPolicy AUTOMATIC 
    XmNotebook gnotebook scrolled bindingType spiral \
	pageChangedCallback {gsv setRawPage g [expr %p-1]}
    Ghostview g gnotebook notebookChildType page pageNumber 1 \
	xdpi 72 ydpi 72
  }
  gsv proc setPageLabels {} {
    $self instvar tabs maxpage pagelabels
    if [info exists tabs] {
      foreach tab $tabs { destroyWidget $tab }
      set tabs {}
    }
    sV gnotebook firstPageNumber 1 lastPageNumber $maxpage
    if {$maxpage > 1} {
      lappend tabs [$self nbTab First major_tab 1]
      set i 1
      foreach label $pagelabels {
	if [expr $i%10==0] { lappend tabs [$self nbTab $label major_tab $i] }
	lappend tabs [$self nbTab $label minor_tab $i]
	incr i
      }
      lappend tabs [$self nbTab Last major_tab $maxpage]
    }
  }
  gsv proc setFocus {} {
    gsv zoom g 1
  }
  gsv proc nbTab {label tabType page} {
    return [XmPushButtonGadget tab$label gnotebook labelString $label \
		notebookChildType $tabType pageNumber $page]
  }
}

#### The code following here is independend from Motif 2.0
gsv proc reload {} {
  $self setFile {} 1
}

gsv proc setFile {f {samePage 0}} {
  $self instvar \
      filename filehandle timeout timeStamp \
      pagedescr pagelabels boundingbox \
      setup_start setup_len prolog_start prolog_len \
      currentpage maxpage 

  set pagedescr     {}
  set pagelabels    {}
  set setup_start   0
  set setup_len     0
  set prolog_start  0
  set prolog_len    0
  set maxpage       1
  set boundingbox   ""

  #puts stderr "setfile $f -----------------------------------------------"
  if [string compare "" $filehandle] {
    #echo closing file
    close $filehandle
    set filehandle ""
  }

  if [string compare "" $f] {
    set filename $f
  }
  sV Reload sensitive 1

  if [string compare "" $timeout] {
    removeTimeOut $timeout
  }
  file lstat $filename stat
  set timeStamp $stat(mtime)
  set timeout [addTimeOut 1000 {gsv autoReload 0}]
   
  $self getPageInfo $filename
  $self setPageLabels

  # we count starting from 0
  incr maxpage -1

  #set samePage 0
  if !$samePage {
    set currentpage 0
  }

  set filehandle [open $filename]
  sV g filename ""
  GhostviewDisableInterpreter g
    
  if !$samePage {
    $self setCurrentPage 1
  }
  
  if [string compare "" $boundingbox] {
   sV g llx [lindex $boundingbox 0] lly [lindex $boundingbox 1] \
	urx [lindex $boundingbox 2] ury [lindex $boundingbox 3] 
  }
  $self refreshCurrentPage g
}

gsv proc checkReload {} {
  $self instvar timeStamp timeout filename
  if [catch {file lstat $filename stat}] return
  if [string compare $timeStamp $stat(mtime)] {
    #puts stderr "+++ must reload..."
    $self reload
  } else {
    #puts stderr "+++ no reload required..."
  }
}

gsv proc autoReload changed {
  $self instvar timeStamp timeout filename
  #puts stderr "+++ auto reload ...."
  if [catch {file lstat $filename stat}] return
  if $changed {
    if [string compare $timeStamp $stat(mtime)] {
      # modification time has changed again
      # puts stderr "+++ doing a a reload later ..."
      set timeStamp $stat(mtime)
      set timeout [addTimeOut 1000 {gsv autoReload 1}]
    } else {
      # modification time is stable again... reload
      # puts stderr "+++ doing a reload..."
      $self reload
    }
  } else {
    if [string compare $timeStamp $stat(mtime)] {
      # modification time has changed
      # puts stderr "+++ must do a reload later ..."
      set timeStamp $stat(mtime)
      set timeout [addTimeOut 1000 {gsv autoReload 1}]
    } else {
      #puts stderr "+++ no reload required ..."
      set timeout [addTimeOut 1000 {gsv autoReload 0}]
    }
  }
}

gsv proc PS_Header {from length} {
  #puts stderr PS_HEADER
  $self instvar setup_start setup_len
  set setup_start $from
  set setup_len   $length
}
gsv proc PS_Page {page label from length} {
  #puts stderr "PS_PAGE $page $label $from $length"
  $self instvar pagedescr pagelabels
  lappend pagedescr "$from $length"
  switch $label {
    label -
    ?  { set label $page }
  }
  lappend pagelabels " $label "
}
gsv proc PS_BoundingBox {llx lly urx ury} {
  #puts stderr "PS_BoundingBox $llx $lly $urx $ury"
  $self instvar boundingbox
  set boundingbox  "$llx $lly $urx $ury"
}

gsv proc getPageInfo {f} {
  $self instvar pagelabels maxpage
  set script {
    $/ = "\n%%";
    $pos = -1;
    $currentPage = 0;
    while(<STDIN>) {
      if (/^Page:\s+(.+)\s+(\d+)\n/) {
	if ($currentPage) {
	  print "gsv PS_Page $currentPage $currentLabel $currentOffset "
	  .($pos-$currentOffset)."\n";
	} else {
	  print "gsv PS_Header 0 ".($pos-1)."\n";
	}
	$currentOffset = $pos;
	$currentPage = $2;
	$currentLabel = $1;
      } elsif (/^BoundingBox:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/ && 
	!$currentPage) {
	print "gsv PS_BoundingBox $1 $2 $3 $4\n";	
      } elsif (/^Trailer/) {
	print "gsv PS_Page $currentPage $currentLabel $currentOffset "
	    .($pos-$currentOffset)."\n";
      }
      $pos += length;
    }
  }
  if [catch {eval [exec perl -e $script < $f]}] {
    set pagelabels " 1 "
  } else {
    set maxpage [llength $pagelabels]
  }
}

gsv proc refreshCurrentPage {w} {
  $self instvar currentpage
  $self setRawPage $w $currentpage
}

gsv proc setRawPage {w page} {
  $self instvar currentpage maxpage filehandle pagedescr
  if ![info exists maxpage] return

  #puts stderr "setRawpage maxpage=$maxpage, page=$page"
  sV Previous-Page sensitive [expr $page>0]
  sV Next-Page sensitive [expr $page<$maxpage]

  if {1 || $maxpage > 0} {
    # puts stderr setRawPage=$page
    set page1 [expr $page+1]
    gsv setPageNumber $page1
    if [GhostviewIsInterpreterReady $w] {
      #puts stderr the-interpreter-is-ready
      GhostviewNextPage $w
    } else {
      $self instvar prolog_start prolog_len setup_start setup_len
      #puts stderr the-interpreter-is-NOT-ready
      GhostviewEnableInterpreter $w
      GhostviewSendPS $w $filehandle $prolog_start $prolog_len 0
      GhostviewSendPS $w $filehandle $setup_start  $setup_len  0
    }
    set pgdes [lindex $pagedescr $page]
    #puts stderr "pgdes = $pgdes"
    catch {GhostviewSendPS $w $filehandle [lindex $pgdes 0] [lindex $pgdes 1] 0}
    set currentpage $page
  } else {
    GhostviewNextPage $w
  }
}

gsv proc nextPage {w offset} {
  $self instvar currentpage maxpage 
  $self checkReload
  if {$maxpage > 1} {
    if [regexp {^[+-]} $offset] {
      # relative offset
      incr currentpage $offset
    } else {
      # absolute value
      set currentpage $offset
    }
    if {$currentpage < 0} {
      set currentpage 0
    } elseif {$currentpage > $maxpage} {
      set currentpage $maxpage
    }
    $self setCurrentPage [expr $currentpage+1]
  } else {
    $self setRawPage g $currentpage
  }
}

gsv proc zoom {w factor} {
  $self scale $w $factor
  $self refreshCurrentPage $w
  $self centerPage
}

gsv proc stop {} {
  GhostviewDisableInterpreter g
  quit
}

gsv proc centerScrollbar {w} {
  XmScrollBarGetValues $w pos size inc pinc
  set max [gV $w maximum]
  if [string compare $max $size] { 
    XmScrollBarSetValues $w [expr ($max-$size)/2] $size $inc $pinc true
  }
}

gsv proc centerPage {} {
  if ![isWidget scrolled] return
  $self centerScrollbar scrolled.VertScrollBar
  $self centerScrollbar scrolled.HorScrollBar
}

gsv create
sV menu_bar menuHelpWidget Help
realize
if [info exists default_ps_doc] {
  gsv setFile $default_ps_doc
}
gsv setFocus

regexp {[^/]*$} $argv0 progName
XmMessageDialog versionbox mph unmanaged \
    dialogTitle "Motif Demo Program using OTcl and the Ghostview widget" \
    okLabelString "Close" \
    messageString "$progName Version 0.3, Aug 20, 1994"

XmMessageDialog helpbox mph unmanaged \
    dialogTitle "$progName Help" \
    okLabelString "Close" \
    messageString "This program is a simple demo program for the Ghostview Widget."

XmFileSelectionDialog fs mp1 unmanaged \
    dialogTitle "Select a Postscript File for Viewing" \
    okLabelString "Load" \
    pattern "*.ps" \
    autoUnmanage true \
    okCallback "gsv setFile %s"

unmanageChild versionbox*Cancel versionbox*Help
unmanageChild helpbox*Cancel    helpbox*Help
unmanageChild fs*Help
