(* This file is part of licht. licht is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. licht is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with licht. If not, see . *) module E:Sym_expr.SYM_EXPR = Evaluate let u = UTF8.from_utf8string let redraw t screen selection = Screen.draw t selection screen; Screen.draw_input t selection screen let action f msg (t, screen, selection) = begin let count = f t in redraw t screen selection; Screen.status screen @@ UTF8.from_utf8string (Printf.sprintf msg count); t, screen, selection end let catalog = Functions.C.compile @@ Functions.built_in Functions.C.empty let f screen = ActionParser.( begin match Screen.read_key screen with | "\027" -> ESC | "\001\002" -> DOWN | "\001\003" -> UP | "\001\004" -> LEFT | "\001\005" -> RIGHT | "\001\006" -> HOME | "\001\104" -> END | "\001R" -> NPAGE | "\001S" -> PPAGE (* See http://www.ibb.net/~anne/keyboard.html for thoses keycode. *) | "\001\074"|"\127" -> DELETE | "e" -> E | "u" -> U | "v" -> V | "y" -> Y | "p" -> P | "=" -> EQUAL | "/" -> SEARCH | ":" -> COMMAND | "\001\154" -> RESIZE | "\001\153" -> begin match Tools.NCurses.get_mouse_event () with | None -> raise Not_found | Some (id, ev, (x, y, z)) -> if Tools.NCurses.is_event_of_type Tools.NCurses.BUTTON1_CLICKED ev then BUTTON1_CLICKED(x, y) else if Tools.NCurses.is_event_of_type Tools.NCurses.BUTTON1_PRESSED ev then BUTTON1_CLICKED(x, y) else if Tools.NCurses.is_event_of_type Tools.NCurses.BUTTON1_RELEASED ev then BUTTON1_RELEASED(x, y) else raise Not_found end | _ -> raise Not_found end) let menhirParser = MenhirLib.Convert.Simplified.traditional2revised ActionParser.normal let parser screen = begin let get_value () = f screen, Lexing.dummy_pos, Lexing.dummy_pos in menhirParser get_value end let rec normal_mode (t, screen, selection) = begin match (parser screen) with | exception x -> normal_mode (t, screen, selection) | Actions.Visual -> Screen.status screen @@ u"-- Selection --"; selection_mode (t, screen, selection) | Actions.Move direction -> begin match Selection.move direction selection with | Some selection -> redraw t screen selection; normal_mode (t, screen, selection) | None -> normal_mode (t, screen, selection) end | Actions.Resize -> normal_mode (t, Screen.resize t selection screen, selection) | Actions.Delete -> let yank_before_delete = fun x -> ignore @@ Sheet.yank selection x; Sheet.delete selection x in normal_mode @@ action yank_before_delete "Deleted %d cells" (t, screen, selection) | Actions.Yank -> normal_mode @@ action (Sheet.yank selection) "Yanked %d cells" (t, screen, selection) | Actions.Paste -> let paste = Sheet.paste @@ Selection.extract selection in normal_mode @@ action paste "Pasted %d cells" (t, screen, selection) | Actions.Undo -> begin match Sheet.undo t with | true -> redraw t screen selection; normal_mode (t, screen, selection) | false -> normal_mode (t, screen, selection) end (* Edit a content *) | Actions.Edit -> let position = Selection.extract selection in let expr, _, _ = Sheet.get_cell position t in let expr_repr = Expression.show expr in begin match Screen.editor ~position ~prefix:(u"-> ") ~init:expr_repr screen with | None -> (* Restore the previous value *) Screen.status screen expr_repr; normal_mode (t, screen, selection) | Some content -> let expr' = Expression.load content in ignore @@ Sheet.add ~history:true expr' position t; redraw t screen selection; normal_mode (t, screen, selection) end (* Insert a new formula, the actual value will be erased *) | Actions.InsertFormula -> let position = Selection.extract selection in begin match Screen.editor ~position ~init:(u"=") screen with | None -> (* Restore the previous value *) let expr, _, _ = Sheet.get_cell position t in Screen.status screen (Expression.show expr); normal_mode (t, screen, selection) | Some content -> let expr = Expression.load content in ignore @@ Sheet.add ~history:true expr position t; redraw t screen selection; normal_mode (t, screen, selection) end | Actions.Button1_clicked coord -> begin match Screen.get_cell screen coord with | None -> normal_mode (t, screen, selection) | Some (x,y) -> begin match Selection.move (Actions.Absolute (x,y)) selection with | Some selection' -> redraw t screen selection'; normal_mode (t, screen, selection') | None -> normal_mode (t, screen, selection) end end | Actions.Button1_released coord -> begin match Screen.get_cell screen coord with | Some (x,y) when (x,y) <> (Selection.extract selection) -> Screen.status screen @@ u"-- Selection -- "; let selection = Selection.extends (Actions.Absolute (x,y)) selection in Screen.draw t selection screen; selection_mode (t, screen, selection) | _ -> normal_mode (t, screen, selection) end | Actions.Command -> begin match Screen.editor ~prefix:(u":") screen with | None -> normal_mode (t, screen, selection) | Some content -> let args = try UTF8.to_utf8string content |> Tools.String.split ~by:' ' with Not_found -> (UTF8.to_utf8string content, "") in command (t, screen, selection) args end | _ -> normal_mode (t, screen, selection) end and selection_mode (t, screen, selection) = begin match (parser screen) with | exception x -> selection_mode (t, screen, selection) | Actions.Resize -> selection_mode (t, Screen.resize t selection screen, selection) | Actions.Delete -> let yank_before_delete = fun x -> ignore @@ Sheet.yank selection x; Sheet.delete selection x in normal_mode @@ action yank_before_delete "Deleted %d cells" (t, screen, selection) | Actions.Yank -> normal_mode @@ action (Sheet.yank selection) "Yanked %d cells" (t, screen, selection) | Actions.Escape -> let selection = Selection.create (Selection.extract selection) in Screen.draw t selection screen; Screen.draw_input t selection screen; Screen.status screen UTF8.empty; normal_mode (t, screen, selection) | Actions.Move m -> let selection = Selection.extends m selection in Screen.draw t selection screen; selection_mode (t, screen, selection) | Actions.Button1_clicked coord -> begin match Screen.get_cell screen coord with | None -> normal_mode (t, screen, selection) | Some (x,y) -> begin match Selection.move (Actions.Absolute (x,y)) selection with | Some selection -> Screen.status screen UTF8.empty; redraw t screen selection; normal_mode (t, screen, selection) | None -> normal_mode (t, screen, selection) end end | _ -> selection_mode (t, screen, selection) end and command (t, screen, selection) action = begin match action with | ("w", file) -> (* Save the file *) Odf.save t file; redraw t screen selection; normal_mode (t, screen, selection) | ("enew", _) -> (* Start a new spreadsheet *) let selection = Selection.create (1, 1) and sheet = Sheet.create catalog in redraw sheet screen selection; normal_mode (sheet, screen, selection) | ("q", _) -> (* Quit *) t | _ -> redraw t screen selection; normal_mode (t, screen, selection) end let () = begin let sheet = if Array.length Sys.argv = 1 then Sheet.create catalog else Odf.load catalog Sys.argv.(1) and selection = Selection.create (1, 1) in Screen.run (fun window -> redraw sheet window selection; ignore @@ normal_mode (sheet, window, selection) ) end