(************************************************************************)
(* * The Coq Proof Assistant / The Coq Development Team *)
(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
(* <O___,, * (see CREDITS file for the list of authors) *)
(* \VV/ **************************************************************)
(* // * This file is distributed under the terms of the *)
(* * GNU Lesser General Public License Version 2.1 *)
(* * (see LICENSE file for the text of the license) *)
(************************************************************************)
let b2c = Ideutils.byte_offset_to_char_offset
class finder name (view : GText.view) =
let widget = Wg_Detachable.detachable
~title:(Printf.sprintf "Find & Replace (%s)" name) () in
let replace_box = GPack.grid (* ~columns:4 ~rows:2 *) ~col_homogeneous:false ~row_homogeneous:false
~packing:widget#add () in
let hb = GPack.hbox ~packing:(replace_box#attach
~left:1 ~top:0 (*~expand:`X ~fill:`X*)) () in
let use_regex =
GButton.check_button ~label:"Regular expression"
~packing:(hb#pack ~expand:false ~fill:true ~padding:3) () in
let use_nocase =
GButton.check_button ~label:"Case insensitive"
~packing:(hb#pack ~expand:false ~fill:true ~padding:3) () in
let _ = GMisc.label ~text:"Find:" ~xalign:1.0
~packing:(replace_box#attach
(*~xpadding:3 ~ypadding:3*) ~left:0 ~top:1 (*~fill:`X*)) () in
let _ = GMisc.label ~text:"Replace:" ~xalign:1.0
~packing:(replace_box#attach
(* ~xpadding:3 ~ypadding:3*) ~left:0 ~top:2 (*~fill:`X*)) () in
let find_entry = GEdit.entry ~editable:true
~packing:(replace_box#attach
(*~xpadding:3 ~ypadding:3*) ~left:1 ~top:1 (*~expand:`X ~fill:`X*)) () in
let replace_entry = GEdit.entry ~editable:true
~packing:(replace_box#attach
(*~xpadding:3 ~ypadding:3*) ~left:1 ~top:2 (*~expand:`X ~fill:`X*)) () in
let next_button = GButton.button ~label:"_Next" ~use_mnemonic:true
~packing:(replace_box#attach (*~xpadding:3 ~ypadding:3*) ~left:2 ~top:1) () in
let previous_button = GButton.button ~label:"_Previous" ~use_mnemonic:true
~packing:(replace_box#attach (*~xpadding:3 ~ypadding:3*) ~left:3 ~top:1) () in
let replace_button = GButton.button ~label:"_Replace" ~use_mnemonic:true
~packing:(replace_box#attach (*~xpadding:3 ~ypadding:3*) ~left:2 ~top:2) () in
let replace_all_button =
GButton.button ~label:"Replace _All" ~use_mnemonic:true
~packing:(replace_box#attach (*~xpadding:3 ~ypadding:3*) ~left:3 ~top:2) () in
object (self)
val mutable last_found = None
method coerce = widget#coerce
method private get_selected_word () =
let start = view#buffer#get_iter `INSERT in
let stop = view#buffer#get_iter `SEL_BOUND in
view#buffer#get_text ~start ~stop ()
method private may_replace () =
(self#search_text <> "") &&
(Str.string_match self#regex (self#get_selected_word ()) 0)
method replace () =
if self#may_replace () then
let txt = self#get_selected_word () in
let () = view#buffer#begin_user_action () in
let _ = view#buffer#delete_selection () in
let _ = view#buffer#insert_interactive (self#replacement txt) in
let () = view#buffer#end_user_action () in
self#find_forward ()
else self#find_forward ()
method private regex =
let rex = self#search_text in
if use_regex#active then
if use_nocase#active then Str.regexp_case_fold rex
else Str.regexp rex
else
if use_nocase#active then Str.regexp_string_case_fold rex
else Str.regexp_string rex
method private replacement txt =
if use_regex#active then Str.replace_matched replace_entry#text txt
else replace_entry#text
method private backward_search starti =
let text = view#buffer#start_iter#get_text ~stop:starti in
let regexp = self#regex in
let offs = (String.length text - 1) in
if offs < 0 then None
else try
let i = Str.search_backward regexp text offs in
let j = Str.match_end () in
Some(view#buffer#start_iter#forward_chars (b2c text i),
view#buffer#start_iter#forward_chars (b2c text j))
with Not_found -> None
method private forward_search starti =
let text = starti#get_text ~stop:view#buffer#end_iter in
let regexp = self#regex in
try
let i = Str.search_forward regexp text 0 in
let j = Str.match_end () in
Some(starti#forward_chars (b2c text i), starti#forward_chars (b2c text j))
with Not_found -> None
method replace_all () =
let rec replace_at (iter : GText.iter) ct tot =
let found = self#forward_search iter in
match found with
| None ->
let tot_str = if Int.equal ct tot then "" else " of " ^ string_of_int tot in
let occ_str = CString.plural tot "occurrence" in
let _ = Ideutils.flash_info ("Replaced " ^ string_of_int ct ^ tot_str ^ " " ^ occ_str) in
()
| Some (start, stop) ->
let text = iter#get_text ~stop:view#buffer#end_iter in
let start_mark = view#buffer#create_mark start in
let stop_mark = view#buffer#create_mark ~left_gravity:false stop in
let mod_save = view#buffer#modified in
let _ = view#buffer#set_modified false in
let _ = view#buffer#delete_interactive ~start ~stop () in
let iter = view#buffer#get_iter_at_mark (`MARK start_mark) in
let _ = view#buffer#insert_interactive ~iter (self#replacement text) in
let edited = view#buffer#modified in
let _ = view#buffer#set_modified (edited || mod_save) in
let next = view#buffer#get_iter_at_mark (`MARK stop_mark) in
let () = view#buffer#delete_mark (`MARK start_mark) in
let () = view#buffer#delete_mark (`MARK stop_mark) in
let next_ct = if edited then ct + 1 else ct in
replace_at next next_ct (tot + 1)
in
let () = view#buffer#begin_user_action () in
let () = replace_at view#buffer#start_iter 0 0 in
view#buffer#end_user_action ()
method private set_not_found () =
find_entry#misc#modify_bg [`NORMAL, `NAME "#F7E6E6"];
method private set_found () =
find_entry#misc#modify_bg [`NORMAL, `NAME "#BAF9CE"]
method private set_normal () =
find_entry#misc#modify_bg [`NORMAL, `NAME "white"]
method private find_from backward ?(wrapped=false) (starti : GText.iter) =
let found =
if backward then self#backward_search starti
else self#forward_search starti in
match found with
| None ->
if not backward && not (starti#equal view#buffer#start_iter) then
self#find_from backward ~wrapped:true view#buffer#start_iter
else if backward && not (starti#equal view#buffer#end_iter) then
self#find_from backward ~wrapped:true view#buffer#end_iter
else
let _ = Ideutils.flash_info "String not found" in
self#set_not_found ()
| Some (start, stop) ->
let text = view#buffer#start_iter#get_text ~stop:view#buffer#end_iter in
let rec find_all offs accum =
if offs > String.length text then
List.rev accum
else try
let i = Str.search_forward self#regex text offs in
let j = Str.match_end () in
find_all (j + 1) (i :: accum)
with Not_found -> List.rev accum
in
let occurs = find_all 0 [] in
let num_occurs = List.length occurs in
(* assoc table of offset, occurrence index pairs *)
let occur_tbl = List.mapi (fun ndx occ -> (occ,ndx+1)) occurs in
let _ = view#buffer#select_range start stop in
let scroll = `MARK (view#buffer#create_mark stop) in
let _ = view#scroll_to_mark ~use_align:false scroll in
let _ =
try
let occ_ndx = List.assoc start#offset occur_tbl in
let occ_str = CString.plural num_occurs "occurrence" in
let wrap_str = if wrapped then
if backward then " (wrapped backwards)"
else " (wrapped)"
else ""
in
Ideutils.flash_info
(string_of_int occ_ndx ^ " of " ^ string_of_int num_occurs ^
" " ^ occ_str ^ wrap_str)
with Not_found ->
CErrors.anomaly (Pp.str "Occurrence of Find string not in table")
in
self#set_found ()
method find_forward () =
let starti = view#buffer#get_iter `SEL_BOUND in
self#find_from false starti
method find_backward () =
let starti = view#buffer#get_iter `INSERT in
self#find_from true starti
method private search_text = find_entry#text
method hide () =
widget#hide;
view#coerce#misc#grab_focus ()
method show () =
widget#show;
find_entry#misc#grab_focus ()
initializer
let _ = self#hide () in
(* Widget button interaction *)
let _ = next_button#connect#clicked ~callback:self#find_forward in
let _ = previous_button#connect#clicked ~callback:self#find_backward in
let _ = replace_button#connect#clicked ~callback:self#replace in
let _ = replace_all_button#connect#clicked ~callback:self#replace_all in
(* Keypress interaction *)
let generic_cb esc_cb ret_cb ev =
let ev_key = GdkEvent.Key.keyval ev in
let (return, _) = GtkData.AccelGroup.parse "Return" in
let (esc, _) = GtkData.AccelGroup.parse "Escape" in
if ev_key = return then (ret_cb (); true)
else if ev_key = esc then (esc_cb (); true)
else false
in
let find_cb = generic_cb self#hide self#find_forward in
let replace_cb = generic_cb self#hide self#replace in
let _ = find_entry#event#connect#key_press ~callback:find_cb in
let _ = replace_entry#event#connect#key_press ~callback:replace_cb in
(* TextView interaction *)
let view_cb ev =
if widget#visible then
let ev_key = GdkEvent.Key.keyval ev in
if ev_key = GdkKeysyms._Escape then (widget#hide; true)
else false
else false
in
let _ = view#event#connect#key_press ~callback:view_cb in
()
end
¤ Dauer der Verarbeitung: 0.67 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|