(***********************************************************************)
(*                                                                     *)
(*                 MLTk, Tcl/Tk interface of OCaml                     *)
(*                                                                     *)
(*    Francois Rouaix, Francois Pessaux, Jun Furuse and Pierre Weis    *)
(*               projet Cristal, INRIA Rocquencourt                    *)
(*            Jacques Garrigue, Kyoto University RIMS                  *)
(*                                                                     *)
(*  Copyright 2002 Institut National de Recherche en Informatique et   *)
(*  en Automatique and Kyoto University.  All rights reserved.         *)
(*  This file is distributed under the terms of the GNU Library        *)
(*  General Public License, with the special exception on linking      *)
(*  described in file LICENSE found in the OCaml source tree.          *)
(*                                                                     *)
(***********************************************************************)
open Camltk

(*
 * Some standard requesters (in Amiga techspeak) or dialog boxes (in Apple
 * jargon).
*)

let version = "$Id$"

(*
 * Simple requester
 *  an entry field, unrestricted, with emacs-like bindings
 * Note: grabs focus, thus always unique at one given moment, and we
 *  shouldn't have to worry about toplevel widget name.
 * We add a title widget in case the window manager does not decorate
 * toplevel windows.
*)

let open_simple title action notaction memory =
  let t = Toplevel.create Widget.default_toplevel [Class "Dialog"] in
  Focus.set t;
  Wm.title_set t title;
  let tit = Label.create t [Text title] in
  let len = max 40 (String.length (Textvariable.get memory)) in
  let e =
    Entry.create t [Relief Sunken; TextVariable memory; TextWidth len] in

  let activate _ =
    let v = Entry.get e in
     Grab.release t;                    (* because of wm *)
     destroy t;                         (* so action can call open_simple *)
     action v in

  bind e [[], KeyPressDetail "Return"] (BindSet ([], activate));

  let f = Frame.create t [] in
  let bok = Button.create f [Text "Ok"; Command activate] in
  let bcancel = Button.create f
            [Text "Cancel";
             Command (fun () -> notaction(); Grab.release t; destroy t)] in

    bind e [[], KeyPressDetail "Escape"]
         (BindSet ([], (fun _ -> Button.invoke bcancel)));
    pack [bok] [Side Side_Left; Expand true];
    pack [bcancel] [Side Side_Right; Expand true];
    pack [tit;e] [Fill Fill_X];
    pack [f] [Side Side_Bottom; Fill Fill_X];
    Frx_widget.resizeable t;
    Focus.set e;
    Tkwait.visibility t;
    Grab.set t

(* A synchronous version *)
let open_simple_synchronous title memory =
  let t = Toplevel.create Widget.default_toplevel [Class "Dialog"] in
  Focus.set t;
  Wm.title_set t title;
  let tit = Label.create t [Text title] in
  let len = max 40 (String.length (Textvariable.get memory)) in
  let e =
    Entry.create t [Relief Sunken; TextVariable memory; TextWidth len] in

  let waiting = Textvariable.create_temporary t in

  let activate _ =
     Grab.release t;                    (* because of wm *)
     destroy t;                         (* so action can call open_simple *)
     Textvariable.set waiting "1" in

  bind e [[], KeyPressDetail "Return"] (BindSet ([], activate));

  let f = Frame.create t [] in
  let bok = Button.create f [Text "Ok"; Command activate] in
  let bcancel =
     Button.create f
        [Text "Cancel";
         Command (fun () ->
                   Grab.release t; destroy t; Textvariable.set waiting "0")] in

    bind e [[], KeyPressDetail "Escape"]
         (BindSet ([], (fun _ -> Button.invoke bcancel)));
    pack [bok] [Side Side_Left; Expand true];
    pack [bcancel] [Side Side_Right; Expand true];
    pack [tit;e] [Fill Fill_X];
    pack [f] [Side Side_Bottom; Fill Fill_X];
    Frx_widget.resizeable t;
    Focus.set e;
    Tkwait.visibility t;
    Grab.set t;
    Tkwait.variable waiting;
    begin match Textvariable.get waiting with
      "1" -> true
    | _ -> false
    end

(*
 * Simple list requester
 * Same remarks as in open_simple.
 * focus seems to be in the listbox automatically
 *)
let open_list title elements action notaction =
  let t = Toplevel.create Widget.default_toplevel [Class "Dialog"] in
  Wm.title_set t title;

  let tit = Label.create t [Text title] in
  let fls = Frame.create t [Relief Sunken; BorderWidth (Pixels 2)] in
  let lb = Listbox.create fls [SelectMode Extended] in
  let sb = Scrollbar.create fls [] in
    Frx_listbox.scroll_link sb lb;
    Listbox.insert lb End elements;

  (* activation: we have to break() because we destroy the requester *)
  let activate _ =
    let l = List.map (Listbox.get lb) (Listbox.curselection lb) in
    Grab.release t;
    destroy t;
    List.iter action l;
    break() in


  bind lb [[Double], ButtonPressDetail 1] (BindSetBreakable ([], activate));

  Frx_listbox.add_completion lb activate;

  let f = Frame.create t [] in
  let bok = Button.create f [Text "Ok"; Command activate] in
  let bcancel = Button.create f
            [Text "Cancel";
             Command (fun () -> notaction(); Grab.release t; destroy t)] in

    pack [bok; bcancel] [Side Side_Left; Fill Fill_X; Expand true];
    pack [lb] [Side Side_Left; Fill Fill_Both; Expand true];
    pack [sb] [Side Side_Right; Fill Fill_Y];
    pack [tit] [Fill Fill_X];
    pack [fls] [Fill Fill_Both; Expand true];
    pack [f] [Side Side_Bottom; Fill Fill_X];
    Frx_widget.resizeable t;
    Tkwait.visibility t;
    Grab.set t


(* Synchronous *)
let open_passwd title =
  let username = ref ""
  and password = ref ""
  and cancelled = ref false in
  let t = Toplevel.create Widget.default_toplevel [Class "Dialog"] in
  Focus.set t;
  Wm.title_set t title;
  let tit = Label.create t [Text title]
  and fu,eu = Frx_entry.new_label_entry t "Username" (fun s -> ())
  and fp,ep = Frx_entry.new_label_entry t "Password" (fun s -> ())
  in
  let fb = Frame.create t [] in
   let bok = Button.create fb
              [Text "Ok"; Command (fun _ ->
                                    username := Entry.get eu;
                                    password := Entry.get ep;
                                    Grab.release t; (* because of wm *)
                                    destroy t)] (* will return from tkwait *)
   and bcancel = Button.create fb
              [Text "Cancel"; Command (fun _ ->
                                    cancelled := true;
                                    Grab.release t; (* because of wm *)
                                    destroy t)] (* will return from tkwait *)
  in
    Entry.configure ep [Show '*'];
    bind eu [[], KeyPressDetail "Return"]
      (BindSetBreakable ([], (fun _ -> Focus.set ep; break())));
    bind ep [[], KeyPressDetail "Return"]
      (BindSetBreakable ([], (fun _ -> Button.flash bok;
                                       Button.invoke bok;
                                       break())));

    pack [bok] [Side Side_Left; Expand true];
    pack [bcancel] [Side Side_Right; Expand true];
    pack [tit;fu;fp;fb] [Fill Fill_X];
    Tkwait.visibility t;
    Focus.set eu;
    Grab.set t;
    Tkwait.window t;
    if !cancelled then failwith "cancelled"
    else (!username, !password)
