open Net_runtime;;
(**********************************************************************)
(* The type of trees *)
(**********************************************************************)
type tree =
{ name : string;
is_directory : bool;
mutable is_visible : bool;
mutable sub : tree array
}
;;
(**********************************************************************)
(* common things *)
(**********************************************************************)
let re_slash = Str.regexp "/";;
let ( !! ) = Buffer.create;; (* abbreviation *)
let message s =
print_string s;
flush stdout
;;
(**********************************************************************)
(* addresses of tree nodes *)
(**********************************************************************)
let string_of_addr addr =
String.concat "/" (List.map string_of_int addr)
;;
let addr_of_string str =
List.map int_of_string (Str.split re_slash str)
;;
let change t addr value =
(* change the is_visible flag in the node of t that is addressed by addr
* to the new 'value'
*)
let rec visit t' =
function
[] -> t'.is_visible <- value
| a::addr -> visit (t'.sub.(a)) addr
in
visit t addr
;;
(**********************************************************************)
(* Convert a tree to an HTML table *)
(**********************************************************************)
let rec width t =
(* finds out the width of the tree *)
let sub_width =
if t.is_visible
then
Array.fold_left (fun m b -> let mb = width b in max m mb) 0 t.sub
else
0
in
sub_width + 1
;;
let convert t =
let width_t = width t in
let text_height = 40 in
let img which height =
"<IMG SRC=\"" ^ which ^ "\" BORDER=0 HEIGHT=" ^
string_of_int height ^ " WIDTH=40 SUPPRESS=\"TRUE\">"
in
let img_v = img "vertical.gif" in
let img_d = img "diagonal.gif" in
let img_vd = img "vert-diag.gif" in
let img_op = img "open.gif" in
let img_cl = img "close.gif" in
let cell s = "<TD>" ^ s ^ "</TD>\n" in
let text_cell s depth =
"<TD VALIGN=BOTTOM COLSPAN=" ^ string_of_int (width_t - depth) ^
">" ^ s ^ "</TD>\n" in
let anchor href s =
"<A HREF=\"" ^ href ^ "\">" ^ s ^ "</A>" in
let rec repeat s =
function
[] -> !! ""
| b :: l' -> Buffer.concat [ !!(if b then s else cell ""); repeat s l']
in
let rec convert_subtree t' addr depth previous is_last_subtree =
let vlines h p = repeat (cell (img_v h)) p in
let previous' =
if depth > 0 then
previous @ [ not is_last_subtree ]
else
previous
in
let this_node =
(* --- first line: label --- *)
Buffer.concat
[ !! "<TR>\n";
begin
(* vertical lines, and a new branch *)
if depth > 0 then begin
Buffer.concat
[ vlines text_height previous;
if is_last_subtree then
!! (cell (img_d text_height))
else
!! (cell (img_vd text_height))
]
end
else !! ""
end;
(* the text *)
!! (text_cell t'.name depth); (* TODO: escape *)
!! "</TR>\n";
(* --- second line: open/close button --- *)
if t'.is_directory then
Buffer.concat
[ !! "<TR>\n";
vlines 26 previous';
begin
if t'.is_visible then
!!
(cell
(anchor
("javascript:parent.caml.change('close_sub_tree','" ^
string_of_addr addr ^ "')")
(img_cl 26)))
else
!!
(cell
(anchor
("javascript:parent.caml.change('open_sub_tree','" ^
string_of_addr addr ^ "')")
(img_op 26)))
end;
!! "</TR>\n"
]
else !! ""
]
(* this_node *)
in
let rec convert_sub_node k =
if k >= Array.length t'.sub - 1 then
convert_subtree t'.sub.(k) (addr @ [k]) (depth+1) previous' true
else
Buffer.concat
[ convert_subtree t'.sub.(k) (addr @ [k]) (depth+1) previous' false;
convert_sub_node (k+1)
]
in
if t'.is_directory & t'.is_visible & Array.length t'.sub > 0 then
Buffer.concat [ this_node; convert_sub_node 0 ]
else
this_node
in
Buffer.concat
[ !! "<HTML><BODY>\n";
!! "<TABLE CELLSPACING=0 BORDER=0 CELLPADDING=0>\n";
convert_subtree t [] 0 [] true;
!! "</TABLE>\n";
!! "</BODY></HTML>\n";
]
;;
(**********************************************************************)
(* sample tree *)
(**********************************************************************)
let sample =
{ name ="First";
is_directory = true;
is_visible = true;
sub = [| { name = "Second";
is_directory = false;
is_visible = false;
sub = [| |] };
{ name = "Third";
is_directory = true;
is_visible = false;
sub = [| |] };
|] }
;;
(**********************************************************************)
(* read tree from file *)
(**********************************************************************)
let read_file name =
let ch = open_in name in
let t = { name = "top"; is_directory = true; is_visible = false;
sub = [| |] } in
let rec enter t' l =
let new_node new_name is_dir =
let t_new = { name = new_name; is_directory = is_dir; is_visible = false;
sub = [| |] } in
let a =
Array.append
t'.sub
[| t_new |]
in
t'.sub <- a;
t_new
in
match l with
[] -> ()
| [ dir_name; "" ] ->
let _ = new_node dir_name true in ()
| [ file_name ] ->
let _ = new_node file_name false in ()
| dir_name :: l' ->
begin try
for i=0 to Array.length t'.sub-1 do
if t'.sub.(i).name = dir_name then
begin enter t'.sub.(i) l'; raise Not_found end
done;
enter (new_node dir_name true) l'
with
Not_found -> ()
end
in
begin try
while true do
let line = input_line ch in
print_string ("line: " ^ line ^ "\n");
flush stdout;
let fields = Str.split re_slash (line ^ "/") in
enter t fields
done;
failwith "never coming to this point"
with
End_of_file ->
close_in ch;
t
| any ->
close_in ch;
raise any
end
;;
(**********************************************************************)
(* Callback functions *)
(**********************************************************************)
(* This is the global variable storing the current tree being edited *)
let global_tree = (ref None : tree option ref);;
let new_tree [| |] =
(* global_tree := Some sample; *)
let t = read_file "tree.txt" in
global_tree := Some t;
Buffer.to_string (convert t)
;;
let open_sub_tree [| addr_string |] =
match !global_tree with
None ->
failwith "Not initialized"
| Some t ->
let addr = addr_of_string addr_string in
change t addr true;
message "changed\n";
let t' = convert t in
message "converted\n";
let s = Buffer.to_string t' in
message "as string\n";
s
;;
let close_sub_tree [| addr_string |] =
match !global_tree with
None ->
failwith "Not initialized"
| Some t ->
let addr = addr_of_string addr_string in
change t addr false;
Buffer.to_string (convert t)
;;
(**********************************************************************)
(* main *)
(**********************************************************************)
let catch_all f args =
try
f args
with
Failure s ->
"Failure " ^ s
| Invalid_argument s ->
"Invalid_argument " ^ s
| Not_found ->
"Not_found"
| End_of_file ->
"End_of_file"
| Sys_error s ->
"Sys_error " ^ s
| Match_failure (s,n,m) ->
"Match_failure " ^ s ^ "," ^ string_of_int n ^ "," ^ string_of_int m
| Assert_failure (s,n,m) ->
"Assert_failure " ^ s ^ "," ^ string_of_int n ^ "," ^ string_of_int m
| _ ->
"Uncaught Ocaml exception"
(* any ->
"Uncaught OCaml exception: " ^ Printexc.to_string any
*)
;;
register_callback "new_tree" 0 (catch_all new_tree);
register_callback "open_sub_tree" 1 (catch_all open_sub_tree);
register_callback "close_sub_tree" 1 (catch_all close_sub_tree);
sleep()
;;