let select_file (mb : Ed_minibuffer.minibuffer) ~title text f =
  let get_user_text () =
    let s = mb#get_user_text in
    let s = Glib.Convert.filename_from_utf8 s in
    let len = String.length s in
    if len > 0 && s.[0] = '~' then
      Printf.sprintf "%s%s"
        Cam_installation.home
        (String.sub s 1 (len - 1))
    else
      s
  in
  let on_complete () =
    let s = get_user_text () in
    try
      let s = Glib.Convert.filename_from_utf8 s in
      let is_dir =
        try (fail_if_unix_error Unix.stat s).Unix.st_kind = Unix.S_DIR
        with Failure _ -> false
      in
      let len = String.length s in
      let (list, text) =
        if is_dir && s.[len-1] = '/' then
          let entries = dir_entries (Filename.dirname s) in
          (entries, s)
        else
          (
           let dir = Filename.dirname s in
           let prefix = Filename.basename s in
           let entries = dir_entries ~prefix dir in
           match max_common entries with
             None -> (["[no match]"], s)
           | Some s ->
               let s = Filename.concat dir s in
               match entries with
                 [_] ->
                   if is_dir then
                     ([], s^"/")
                   else
                     ([], s)
               | _ -> (entries, s)
          )
      in
      mb#set_text
        ~list: (List.map Glib.Convert.filename_from_utf8 list)
        ~fixed: (title^": ")
        (Glib.Convert.filename_to_utf8 text)
    with
      Failure err ->
        mb#set_text ~list: [to_utf8 err] ~fixed: (title^": ") s
  in
  mb#clear;
  let on_eval () =
    let s = Glib.Convert.filename_from_utf8 (get_user_text ())in
    mb#set_text "";
    mb#set_active false;
    match s with
      "" -> ()
    | _ -> f s
  in
  mb#set_text ~fixed: (title^": ") text;
  mb#set_on_eval on_eval;
  mb#set_on_complete on_complete;
  mb#set_history select_file_history;
  mb#set_active true