REBOL [
   Title: "Timeline"
   Date:  1999-10-5
   Author: ["Ingo Hohmann" "iho"]
   email: [EMAIL PROTECTED]
   site: http://www.2b1.de/Rebol/
   Purpose: "Prints a timetable"
   File: %Timeline.r
   Rights: {(c) 1999 Ingo Hohmann - free for any use, except that
       modifications to this script will have to stay free.
       If you improve this script, please tell me about it.}
   category: 'script
   version: 0.9.2
   status: 'beta
   usage: {
When started the program asks for a 
- name for an individual appentmoint,
- weekday ( mo, tu, ... )
- and the time, from, to 
  in the format "hhmm-hhmm", 
  possibly adding ":hhmm" to show that an appointment may
      be relocated in the specified timegap.
examples:
name: Meet boss
weekday: mo
time: 1000-1200
=> Meet the boss at monday, from 10:00 to 12:00
name: go shopping
weekday: sa
time: 900-1600/230
=> Go shopping at saturday, in the time range from 9:00 to 16:00,
   but only for 2 1/2 hours
}
   history: [
         [0.9.2 5-10-1999 {iho} {- asks to save before exit, 
             - display char can be changed
             - weekday "nn" for unknown days
        - times like 10:30 are automatically converted to 1030}]
         [0.9.1 4-10-1999 {iho} {errors in Latex output corrected} ]
         [0.9.0 15-9-1999 {iho} {first public release} ]
   ]

   KnownBugs: [
      {lines longer than your screenwidth mess up the display on
      terminals (e.g. under Linux)}
   ]

   ToDo: [
      {I need more error testing}
      {a better way to show different days ...}
      {a way to show, that the same entry takes place on different
      days/times (ANDed / ORed)}
      {add a custom header}
   ]
]

;
; try loading my modules, if not use ...
;

if error? try [
   module 'Timeline.r
   import %iho-tools.r 
] [

   ;
   ; Functions, etc. copied over for distribution ...
   ;

   ; author: Bohdan Lechnowsky <[EMAIL PROTECTED]>
   align: function [
      "Forms data into columns with optional alignment"
      data length /left /right /center] [len] [
      if right [
         return head copy/part 
         tail insert/dup head form data " " length 
            (length * -1)
      ]
      if center [
         data: head insert/dup head form data " " len: (length / 2)
         data: head insert/dup tail data " " len
         return copy/part at data ((length? data) / 2 - len + 1) length
      ]
      return copy/part head insert/dup tail form data " " length length
   ]

   ;
   ; User Interface
   ;

   menu-object: make object! [

      header: copy "^LHelp" 
      menus: copy []

      init: func [ "Initialize menu" /data men [block!]] [
         either data [menus: copy men]
            [menus: copy []]
      ] ; init

      FIXME: "add /at position  refinement"
      add: func [ "Adds a new menuline"
         key [char! none!] help [string! none!] action
            [block! none!]] [
         append menus key
         append menus help
         insert/only tail menus action
      ] ; add

      show: func [ "Shows the menu" ] [
         print rejoin [ header ]
         foreach [key help action] menus [
            prin either char? key
               [rejoin ["("key") "]]
               ["    "]
            print either string? help
               [help]
               [""]
         ]
         print " "
      ] ; show

      ask: func [ "Waits for a keypress, and DOes menu action" 
         /local con c key help action ] [
         con: open/binary [scheme: 'console]
         wait con
         c: to-char to-integer copy con
         foreach [key help action] menus [
            if key == c [
               if error? try [
                  if error? err: try action [
                     print rejoin ["Error in menu function: " action]
                     if confirm "... would you like to see it (y/N)? " [
                        print mold disarm err
                        self/ask
                     ]
                     return 'error
                  ] [ 
                     'none
                  ] 
               ] [
                  return 'ok
               ]
            ] 
         ]
         return 'not_found
      ] ; ask

      loop: func [ {After keypress, starts waiting for the next key,
           you'd better have a halt in one of your menu actions}
         /show "always show the menu, before waiting for keypress" 
         /do "always do action before waiting" 
         todo [block!] "action to do"] [
         while [ true ] [
            if show [ self/show ]
            if do   [ if error? try todo [
                  print "Error while doing menu action!" 
               ] ]
            ask
         ]
      ] ; loop    
   ] ; make menu

   ;
   ; helpers
   ;

   div: func ["integer division" dividend divisor ] [
      to-integer (to-integer dividend) / to-integer divisor
   ]     

   mod: func ["remainder" dividend divisor ] [
         (to-integer dividend) // to-integer divisor
   ]     

   prompt: func [prompts defaults /local prom] [
      prom: copy prompts
      append prom defaults
      ask head insert/dup tail prom "^H" length? defaults
   ]

] ; if error?


;
; END of Functions, etc. copied over for distribution ...
;

menu: make menu-object []
menu/init/data [
   #"l" "load data" [load-data]
   #"s" "Save data" [save-data]
   #"i" "Input data"[input-data]
   #"e" "Edit data" [edit-data]
   #"d" "Display"   [show-data]
   #"c" "Change header" [change-header]
   #"o" "Output data ..."    [output-menu/show output-menu/ask]
   none none none
   #"h" "help"      [menu/show menu/ask]
   #"q" "quit Timeline" [quit-timeline]
]

output-menu: make menu-object []
output-menu/init/data [
   #"t" "output as Txt"   [output-data/txt]
   #"l" "output as Latex" [output-data/latex]
   #"h" "output as html"  [output-data/html]
]

quit-timeline: func[ "Quits timeline, asks if you want to save"] [
   if all [dirty confirm "Data has been changed, save first? (y/N) "] [
      save-data
   ]
   halt
]

change-header: func[ "Changes the header-line"] [
   print "Please change the Header-line to your wishes: "
   change at header length? header #""
   header: append prompt "" header  #"^/" 
   FIXME: {ask/prompt kill leading spaces, use input instead}
]

load-data: func [] [
   data_file: to-file prompt "LOAD File: " rejoin [
      any [data_file %TimelineData.r]
   ]

   data_list: copy either exists? data_file [ load data_file ] [ [] ]
]

save-data: func [] [
   data_file: to-file prompt "SAVE to File: " data_file

   save data_file data_list 
   dirty: false
]

input-data: func [/local name date time char] [
   while [1][
      print "Please input your data:"
      name: ask "Name: "
      if name == "" [break]
      day:  prompt "Day:  " day
      time: rejoin parse ask "Time: " ":" 
      char: prompt "Show: " def-char
      if not 1 = length? char [char: to-string char/1]

      entry: compose [ (day) (time) (name) (char) ]
      dirty: true
      insert/only tail data_list entry
   ]
   sort-data
]

edit-data: func [/local num name date time entry char] [
   num: to-integer ask "Number of entry to edit: "

   if num > (length? data_list) [ exit ] ; ==========>

   print "Please update your data:"
   name: prompt "Name: " third pick data_list num
   day:  prompt "Day:  " first pick data_list num
   time: rejoin parse (prompt "Time: " (second pick data_list num)) ":"  
   char: prompt "Show: " either char: pick pick data_list num 4 
      [char]
      [def-char]
   if not 1 = length? char [char: to-string char/1]

   entry: compose [ (day) (time) (name) (char) ]
   dirty: true
   change/only at data_list num entry
]

show-data: func [] [
   print append copy "^L" create-txt-output/edit
]

output-data: func [
   /txt "output as txt"
   /latex "output as latex" 
   /html "output as html"
   /local file out] [

   if txt   [ file: "TimelineOUT.txt" ]
   if latex [ file: "TimelineOUT.tex" ]
   if html  [ file: "TimelineOUT.html" ]

   file: to-file prompt "Filename to save to: " file   

   if txt   [out: create-txt-output]
   if latex [out: create-latex-verbatim-output]
   if html  [out: create-html-pre-output]

   write file out 
]

;
; Program help 
;


FIXME: {use words and aliases for weekdays? 
       Maybe better for different languages?}
weekdays: [ "mo" "di" "mi" "do" "fr" "sa" "so" "nn"]
;weekdays: [ "mo" "tu" "we" "th" "fr" "sa" "su" "nn"] ;this ok for english?

start:  8 ; start time of timeline
end:   22 ; end time of timeline, not used till now
day:  "mo"        ; "default" day
dirty: false      ; has data been changed?
def-char: "x" ; default display character
range-char: "-"   ; character show timerange to relocate
collision-char: "~" ; display colliding 
Header: "                           Timeline^/" ; header to display

;
; Program logic
;

sort-data: func [] [
   sort/compare data_list func [a b] [ 
         (to-integer (pick (parse (pick a 2) " -") 1)) 
      < (to-integer (pick (parse (pick b 2) " -") 1))
   ]
]

FIXME: {still to be done, priorites would be nice}
detect-collision: func[ "Detects collisions in the Timeline"
   /local last
] [
   last: false
   ; works on sorted list
   foreach day weekdays [
      foreach data data_list [
         if find (first data) day [
            if last [
            ]   
         ]
      ]
   ]
]   

characters: func [times char][
   ret: copy ""
   insert/dup ret char times
   ret
]


create-txt-output: func [/edit /local txt idx times offset data char] [
   txt: append copy header
   "   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22^/" 
   foreach day weekdays [
      append txt rejoin [ 
         day 
         " |   |   |   |   |   |   |   |   |   |   |   |   |   |   |^/" 
      ]
      idx: 0
      foreach data data_list [
         idx: idx + 1
         if find (first data) day [
            times: parse data/2 "- ;/"
            offset: (((div times/1 100) - start) * 4) 
               + ((mod times/1 100) / 15)
            either (length? times) = 2 [ 
               length: (((div times/2 100) - start) * 4) 
               + ((mod times/2 100) / 15) - offset
               range: 0
            ] [
               length: ((div times/3 100) * 4) 
               + ((mod times/3 100) / 15); - offset
               range: (((div times/2 100) - start) * 4) 
               + ((mod times/2 100) / 15) - offset - length
            ]
            append txt rejoin ["  "
               characters offset + 1 " "
               characters length either char: pick data 4 [char] [def-char]
               characters range range-char
               characters 60 - offset - length - range " "
               either edit 
                  [ rejoin ["(" align/right idx 3 ") "] ]
                  [ "" ]
               data/3 "^/"
            ] 
         ]
      ]
   ]
   txt
]

create-latex-verbatim-output: func [] [
   ; landscape seems not to work ...
   rejoin [
      "\documentclass[ 10pt, a4paper, german, landscape]{article}^/"
      "\begin{document}^/"
      "\begin{verbatim}^/"

      create-txt-output

      "\end{verbatim}^/"
      "\end{document}^/"
   ]
]

create-html-pre-output: func [] [
   rejoin [
      {<!doctype html public "-//w3c//dtd html 3.2 final//en">^/}
      "<html>^/"
      "<head>^/"
      "<title>Timeline (c) Ingo Hohmann</title>^/"
      "</head>^/"
      "<body>^/"
      "<pre>^/"

      create-txt-output

      "</pre>^/"
      "</body>^/"
      "</html>^/"
   ]
]

print "^L      Timeline.r (c) Ingo Hohmann^/"
data_file: none
load-data

menu/loop/do [show-data]

halt




none

Reply via email to