+class clickableMathView obj =
+let text_width = 80 in
+let dummy_loc = DisambiguateTypes.dummy_floc in
+object (self)
+ inherit GMathViewAux.multi_selection_math_view obj
+
+ val mutable href_callback: (string -> unit) option = None
+ method set_href_callback f = href_callback <- f
+
+ val mutable _cic_info = None
+ method private set_cic_info info = _cic_info <- info
+ method private cic_info = _cic_info
+
+ initializer
+ self#set_font_size !current_font_size;
+ ignore (self#connect#selection_changed self#choose_selection_cb);
+ ignore (self#event#connect#button_press self#button_press_cb);
+ ignore (self#event#connect#button_release self#button_release_cb);
+ ignore (self#event#connect#selection_clear self#selection_clear_cb);
+ ignore (self#coerce#misc#connect#selection_get self#selection_get_cb)
+
+ val mutable button_press_x = -1.
+ val mutable button_press_y = -1.
+ val mutable selection_changed = false
+
+ method private selection_get_cb ctxt ~info ~time =
+ let text =
+ match ctxt#target with
+ | "PATTERN" -> self#text_of_selection `Pattern
+ | "TERM" | _ -> self#text_of_selection `Term
+ in
+ match text with
+ | None -> ()
+ | Some s -> ctxt#return s
+
+ method private text_of_selection fmt =
+ match self#get_selections with
+ | [] -> None
+ | node :: _ -> Some (self#string_of_node ~paste_kind:fmt node)
+
+ method private selection_clear_cb sel_event =
+ self#remove_selections;
+ (GData.clipboard Gdk.Atom.clipboard)#clear ();
+ false
+
+ method private button_press_cb gdk_button =
+ let button = GdkEvent.Button.button gdk_button in
+ if button = left_button then begin
+ button_press_x <- GdkEvent.Button.x gdk_button;
+ button_press_y <- GdkEvent.Button.y gdk_button;
+ selection_changed <- false
+ end else if button = right_button then
+ self#popup_contextual_menu (GdkEvent.Button.time gdk_button);
+ false
+
+ (** @return a pattern structure which contains pretty printed terms *)
+ method private tactic_text_pattern_of_selection =
+ match self#get_selections with
+ | [] -> assert false (* this method is invoked only if there's a sel. *)
+ | node :: _ ->
+ let id = id_of_node node in
+ let cic_info, unsh_sequent = self#get_cic_info id in
+ match self#get_term_by_id cic_info id with
+ | SelTerm (t, father_hyp) ->
+ let sequent = self#sequent_of_id ~paste_kind:`Pattern id in
+ let text = self#string_of_cic_sequent sequent in
+ (match father_hyp with
+ | None -> None, [], Some text
+ | Some hyp_name -> None, [ hyp_name, text ], None)
+ | SelHyp (hyp_name, _ctxt) -> None, [ hyp_name, "%" ], None
+
+ method private popup_contextual_menu time =
+ let menu = GMenu.menu () in
+ let add_menu_item ?(menu = menu) ?stock ?label () =
+ GMenu.image_menu_item ?stock ?label ~packing:menu#append () in
+ let check = add_menu_item ~label:"Check" () in
+ let reductions_menu_item = GMenu.menu_item ~label:"βδιζ-reduce" () in
+ menu#append reductions_menu_item;
+ let reductions = GMenu.menu () in
+ reductions_menu_item#set_submenu reductions;
+ let normalize = add_menu_item ~menu:reductions ~label:"Normalize" () in
+ let reduce = add_menu_item ~menu:reductions ~label:"Reduce" () in
+ let simplify = add_menu_item ~menu:reductions ~label:"Simplify" () in
+ let whd = add_menu_item ~menu:reductions ~label:"Weak head" () in
+ menu#append (GMenu.separator_item ());
+ let copy = add_menu_item ~stock:`COPY () in
+ let gui = get_gui () in
+ List.iter (fun item -> item#misc#set_sensitive gui#canCopy)
+ [ copy; check; normalize; reduce; simplify; whd ];
+ let reduction_action kind () =
+ let pat = self#tactic_text_pattern_of_selection in
+ let statement =
+ let loc = DisambiguateTypes.dummy_floc in
+ "\n" ^
+ GrafiteAstPp.pp_executable ~term_pp:(fun s -> s)
+ ~lazy_term_pp:(fun _ -> assert false) ~obj_pp:(fun _ -> assert false)
+ (GrafiteAst.Tactical (loc,
+ GrafiteAst.Tactic (loc, GrafiteAst.Reduce (loc, kind, pat)),
+ Some (GrafiteAst.Semicolon loc))) in
+ (MatitaScript.current ())#advance ~statement () in
+ connect_menu_item copy gui#copy;
+ connect_menu_item normalize (reduction_action `Normalize);
+ connect_menu_item reduce (reduction_action `Reduce);
+ connect_menu_item simplify (reduction_action `Simpl);
+ connect_menu_item whd (reduction_action `Whd);
+ menu#popup ~button:right_button ~time
+
+ method private button_release_cb gdk_button =
+ if GdkEvent.Button.button gdk_button = left_button then begin
+ let button_release_x = GdkEvent.Button.x gdk_button in
+ let button_release_y = GdkEvent.Button.y gdk_button in
+ if selection_changed then
+ ()
+ else (* selection _not_ changed *)
+ if near (button_press_x, button_press_y)
+ (button_release_x, button_release_y)
+ then
+ let x = int_of_float button_press_x in
+ let y = int_of_float button_press_y in
+ (match self#get_element_at x y with
+ | None -> ()
+ | Some elt ->
+ let localName = href_ds in
+ if elt#hasAttributeNS ~namespaceURI:xlink_ns ~localName then
+ self#invoke_href_callback
+ (elt#getAttributeNS ~namespaceURI:xlink_ns
+ ~localName)#to_string
+ gdk_button
+ else
+ ignore (self#action_toggle elt));
+ end;
+ false
+
+ method private invoke_href_callback href_value gdk_button =
+ let button = GdkEvent.Button.button gdk_button in
+ if button = left_button then
+ let time = GdkEvent.Button.time gdk_button in
+ match href_callback with
+ | None -> ()
+ | Some f ->
+ (match HExtlib.split href_value with
+ | [ uri ] -> f uri
+ | uris ->
+ let menu = GMenu.menu () in
+ List.iter
+ (fun uri ->
+ let menu_item =
+ GMenu.menu_item ~label:uri ~packing:menu#append () in
+ connect_menu_item menu_item (fun () -> f uri))
+ uris;
+ menu#popup ~button ~time)
+
+ method private choose_selection_cb gdome_elt =
+ let set_selection elt =
+ let misc = self#coerce#misc in
+ self#set_selection (Some elt);
+ misc#add_selection_target ~target:"STRING" Gdk.Atom.primary;
+ ignore (misc#grab_selection Gdk.Atom.primary);
+ in
+ let rec aux elt =
+ if (elt#getAttributeNS ~namespaceURI:helm_ns
+ ~localName:xref_ds)#to_string <> ""
+ then
+ set_selection elt
+ else
+ try
+ (match elt#get_parentNode with
+ | None -> assert false
+ | Some p -> aux (new Gdome.element_of_node p))
+ with GdomeInit.DOMCastException _ -> ()
+ in
+ (match gdome_elt with
+ | Some elt when (elt#getAttributeNS ~namespaceURI:xlink_ns
+ ~localName:href_ds)#to_string <> "" ->
+ set_selection elt
+ | Some elt -> aux elt
+ | None -> self#set_selection None);
+ selection_changed <- true
+
+ method update_font_size = self#set_font_size !current_font_size
+
+ (** find a term by id from stored CIC infos @return either `Hyp if the id
+ * correspond to an hypothesis or `Term (cic, hyp) if the id correspond to a
+ * term. In the latter case hyp is either None (if the term is a subterm of
+ * the sequent conclusion) or Some hyp_name if the term belongs to an
+ * hypothesis *)
+ method private get_term_by_id cic_info id =
+ let unsh_item, ids_to_terms, ids_to_hypotheses, ids_to_father_ids, _, _ =
+ cic_info in
+ let rec find_father_hyp id =
+ if Hashtbl.mem ids_to_hypotheses id
+ then Some (name_of_hypothesis (Hashtbl.find ids_to_hypotheses id))
+ else
+ let father_id =
+ try Hashtbl.find ids_to_father_ids id
+ with Not_found -> assert false in
+ match father_id with
+ | Some id -> find_father_hyp id
+ | None -> None
+ in
+ try
+ let term = Hashtbl.find ids_to_terms id in
+ let father_hyp = find_father_hyp id in
+ SelTerm (term, father_hyp)
+ with Not_found ->
+ try
+ let hyp = Hashtbl.find ids_to_hypotheses id in
+ let _, context, _ =
+ match unsh_item with Some seq -> seq | None -> assert false in
+ let context' = MatitaMisc.list_tl_at hyp context in
+ SelHyp (name_of_hypothesis hyp, context')
+ with Not_found -> assert false
+
+ method private find_obj_conclusion id =
+ match self#cic_info with
+ | None
+ | Some (_, _, _, _, _, None) -> assert false
+ | Some (_, ids_to_terms, _, ids_to_father_ids, ids_to_inner_types, Some annobj) ->
+ let id =
+ find_root_id annobj id ids_to_father_ids ids_to_terms ids_to_inner_types
+ in
+ (try Hashtbl.find ids_to_terms id with Not_found -> assert false)
+
+ method private string_of_node ~(paste_kind:paste_kind) node =
+ if node#hasAttributeNS ~namespaceURI:helm_ns ~localName:xref_ds
+ then
+ let id = id_of_node node in
+ self#string_of_cic_sequent (self#sequent_of_id ~paste_kind id)
+ else string_of_dom_node node
+
+ method private string_of_cic_sequent cic_sequent =
+ let script = MatitaScript.current () in
+ let metasenv =
+ if script#onGoingProof () then script#proofMetasenv else [] in
+ let _, (acic_sequent, _, _, ids_to_inner_sorts, _) =
+ Cic2acic.asequent_of_sequent metasenv cic_sequent in
+ let _, _, _, annterm = acic_sequent in
+ let ast, ids_to_uris =
+ TermAcicContent.ast_of_acic ids_to_inner_sorts annterm in
+ let pped_ast = TermContentPres.pp_ast ast in
+ let markup = CicNotationPres.render ids_to_uris pped_ast in
+ BoxPp.render_to_string text_width markup
+
+ method private pattern_of term context unsh_sequent =
+ let context_len = List.length context in
+ let _, unsh_context, conclusion = unsh_sequent in
+ try
+ (match
+ List.nth unsh_context (List.length unsh_context - context_len - 1)
+ with
+ | None -> assert false (* can't select a restricted hypothesis *)
+ | Some (name, Cic.Decl ty) ->
+ ProofEngineHelpers.pattern_of ~term:ty [term]
+ | Some (name, Cic.Def (bo, _)) ->
+ ProofEngineHelpers.pattern_of ~term:bo [term])
+ with Failure _ | Invalid_argument _ ->
+ ProofEngineHelpers.pattern_of ~term:conclusion [term]
+
+ method private get_cic_info id =
+ match self#cic_info with
+ | Some ((Some unsh_sequent, _, _, _, _, _) as info) -> info, unsh_sequent
+ | Some ((None, _, _, _, _, _) as info) ->
+ let t = self#find_obj_conclusion id in
+ info, (~-1, [], t) (* dummy sequent for obj *)
+ | None -> assert false
+
+ method private sequent_of_id ~(paste_kind:paste_kind) id =
+ let cic_info, unsh_sequent = self#get_cic_info id in
+ let cic_sequent =
+ match self#get_term_by_id cic_info id with
+ | SelTerm (t, _father_hyp) ->
+ let occurrences =
+ ProofEngineHelpers.locate_in_conjecture t unsh_sequent in
+ (match occurrences with
+ | [ context, _t ] ->
+ (match paste_kind with
+ | `Term -> ~-1, context, t
+ | `Pattern -> ~-1, [], self#pattern_of t context unsh_sequent)
+ | _ ->
+ HLog.error (sprintf "found %d occurrences while 1 was expected"
+ (List.length occurrences));
+ assert false) (* since it uses physical equality *)
+ | SelHyp (_name, context) -> ~-1, context, Cic.Rel 1 in
+ cic_sequent
+
+ method private string_of_selection ~(paste_kind:paste_kind) =
+ match self#get_selections with
+ | [] -> None
+ | node :: _ -> Some (self#string_of_node ~paste_kind node)
+
+ method has_selection = self#get_selections <> []
+
+ (** @return an associative list format -> string with all possible selection
+ * formats. Rationale: in order to convert the selection to TERM or PATTERN
+ * format we need the sequent, the metasenv, ... keeping all of them in a
+ * closure would be more expensive than keeping their already converted
+ * forms *)
+ method strings_of_selection =
+ try
+ let misc = self#coerce#misc in
+ List.iter
+ (fun target -> misc#add_selection_target ~target Gdk.Atom.clipboard)
+ [ "TERM"; "PATTERN"; "STRING" ];
+ ignore (misc#grab_selection Gdk.Atom.clipboard);
+ List.map
+ (fun paste_kind ->
+ paste_kind, HExtlib.unopt (self#string_of_selection ~paste_kind))
+ [ `Term; `Pattern ]
+ with Failure _ -> failwith "no selection"