type state =
  | INIT
  | FIRST_PRESS
  | FIRST_RELEASE
  | SECOND_PRESS
  | FINISHED

class curve parent cb = object (self)
  val mutable state = INIT
  val points = Array.make 8 0.
  val item = 
    GnoCanvas.bpath parent
      ~props:[ `OUTLINE_COLOR "blue" ;
	       `WIDTH_PIXELS 5 ; 
	       `CAP_STYLE `ROUND ]

  method click ev =
    let x = GdkEvent.Button.x ev in
    let y = GdkEvent.Button.y ev in
    match GdkEvent.get_type ev with
    | `BUTTON_PRESS when state = INIT ->
	points.(0) <- x ;
	points.(1) <- y ;
	state <- FIRST_PRESS
    | `BUTTON_RELEASE when state = FIRST_PRESS ->
	points.(2) <- x ;
	points.(3) <- y ;
	let path = GnomeCanvas.PathDef.new_path () in
	GnomeCanvas.PathDef.moveto path points.(0) points.(1) ;
	GnomeCanvas.PathDef.lineto path points.(2) points.(3) ;
	item#set [ `BPATH path ] ;
	item#show () ; 
	state <- FIRST_RELEASE
    | `BUTTON_PRESS when state = FIRST_RELEASE ->
	points.(4) <- x ;
	points.(5) <- y ;
	let path = GnomeCanvas.PathDef.new_path () in
	GnomeCanvas.PathDef.moveto path points.(0) points.(1) ;
	GnomeCanvas.PathDef.curveto path 
	  points.(4) points.(5) 
	  points.(4) points.(5) 
	  points.(2) points.(3) ;
	item#set [ `BPATH path ] ;
	state <- SECOND_PRESS
    | `BUTTON_PRESS when state = SECOND_PRESS ->
	points.(6) <- x ;
	points.(7) <- y ;
	let path = GnomeCanvas.PathDef.new_path () in
	GnomeCanvas.PathDef.moveto path points.(0) points.(1) ;
	GnomeCanvas.PathDef.curveto path 
	  points.(4) points.(5) 
	  points.(6) points.(7) 
	  points.(2) points.(3) ;
	item#set [ `BPATH path ] ;
    	state <- FINISHED
    | _ -> ()

  method motion ev =
    let x = GdkEvent.Motion.x ev in
    let y = GdkEvent.Motion.y ev in
    if state = FIRST_PRESS
    then begin
      points.(2) <- x ;
      points.(3) <- y ;
      let path = GnomeCanvas.PathDef.new_path () in
      GnomeCanvas.PathDef.moveto path points.(0) points.(1) ;
      GnomeCanvas.PathDef.lineto path points.(2) points.(3) ;
      item#set [ `BPATH path ] ;
    end

  method is_not_complete = 
    state <> FINISHED

  method kill () = 
    item#destroy () ;
    state <- FINISHED

  initializer 
    let _ = item#connect#event (cb self) in
    ()
end

      
let item_event curve ev =
  match ev with
  | `BUTTON_PRESS ev ->
      if GdkEvent.Button.button ev = 1 &&
	Gdk.Convert.test_modifier `SHIFT (GdkEvent.Button.state ev)
      then (curve#kill () ; true)
      else false
  | _ -> false



let canvas_event curves root ev =
  match ev with
  | `BUTTON_PRESS ev when GdkEvent.Button.button ev = 1 ->
      let curve = 
	match !curves with
	| Some b when b#is_not_complete -> b
	| _ -> 
	    let c = new curve root item_event in
	    curves := Some c ; c
      in
      curve#click ev ;
      false
  | `BUTTON_RELEASE ev when GdkEvent.Button.button ev = 1 -> 
      begin match !curves with
      | Some b when b#is_not_complete -> 
	  b#click ev
      | _ -> ()
      end ; 
      false
  | `MOTION_NOTIFY ev -> 
      begin match !curves with
      | Some b when b#is_not_complete -> 
	  b#motion ev ; true
      | _ -> false
      end
  | _ -> false



let create_canvas ~aa cont =
  let frame = GBin.frame ~shadow_type:`IN ~packing:cont#add () in
  let canvas = GnoCanvas.canvas ~aa ~width:600 ~height:250 ~packing:frame#add () in
  canvas#set_scroll_region 0. 0. 600. 250. ;
  let r = GnoCanvas.rect canvas#root
      ~props:[ `OUTLINE_COLOR "black" ;
	       `FILL_COLOR "white" ;
	       `X1 0.; `Y1 0. ; `X2 600. ; `Y2 250. ] in
  let t = GnoCanvas.text canvas#root
      ~props:[ `TEXT (if aa then "AntiAlias" else "Non-AntiAlias") ;
	       `X 270. ; `Y 5. ;
	       `FONT "Sans 12" ;
	       `ANCHOR `NORTH ;
	       `FILL_COLOR "black" ] in
  r#connect#event (canvas_event (ref None) canvas#root)



let create_canvas_bezier_curve window =
  let vbox = GPack.vbox ~border_width:4 ~packing:window#add () in
  GMisc.label 
    ~text:"Drag a line with button 1. Then mark 2 control points with\n\
           button 1. Shift+click with button 1 to destroy the curve.\n"
    ~packing:vbox#add () ;
  create_canvas ~aa:false vbox ;
  create_canvas ~aa:true vbox



let main_1 () =
  let window = GWindow.window () in
  create_canvas_bezier_curve window ;
  window#connect#destroy ~callback:GMain.Main.quit ;
  window#show () ;
  GMain.Main.main ()

let _ = 
   main_1 ()



(* Local Variables: *)
(* compile-command: "ocamlopt -w s -i -I ../../src lablgtk.cmxa gtkInit.cmx lablgnomecanvas.cmxa canvas-curve.ml" *)
(* End: *)
