simple_editor/
contextmenu.rs

1//! Explorer context menu: "Edit with Simple Editor" for video files (HKCU, no admin needed).
2//! Appears in the classic menu ("Show more options" on Windows 11). Registered per extension because
3//! several video extensions (.mov/.m4v/.flv/.ts/.mts/.mpg) have no PerceivedType=video.
4
5use std::io::ErrorKind;
6use winreg::enums::HKEY_CURRENT_USER;
7use winreg::RegKey;
8
9const VIDEO_EXTS: &[&str] = &[
10    "mp4", "mov", "mkv", "webm", "avi", "m4v", "wmv", "ts", "m2ts", "mts", "flv", "3gp", "mpg", "mpeg", "gif", "ogv",
11    "vob", "divx", "asf", "f4v", "dv", "mxf",
12];
13/// Written by older builds; removed on uninstall.
14const LEGACY_KEY: &str = r"Software\Classes\SystemFileAssociations\video\shell\SimpleEditor";
15
16fn key(ext: &str) -> String {
17    format!(r"Software\Classes\SystemFileAssociations\.{ext}\shell\SimpleEditor")
18}
19
20fn exe() -> String {
21    std::env::current_exe().map(|p| p.to_string_lossy().into_owned()).unwrap_or_default()
22}
23
24/// True if the menu entry exists and points at this executable.
25pub fn is_installed() -> bool {
26    RegKey::predef(HKEY_CURRENT_USER)
27        .open_subkey(format!(r"{}\command", key("mp4")))
28        .ok()
29        .and_then(|k| k.get_value::<String, _>("").ok())
30        .map(|v| v.to_ascii_lowercase().contains(&exe().to_ascii_lowercase()))
31        .unwrap_or(false)
32}
33
34pub fn install() -> std::io::Result<()> {
35    let hk = RegKey::predef(HKEY_CURRENT_USER);
36    let _ = hk.delete_subkey_all(LEGACY_KEY); // would duplicate the entry for .mp4 etc.
37    let exe = exe();
38    for ext in VIDEO_EXTS {
39        let key = key(ext);
40        let (k, _) = hk.create_subkey(&key)?;
41        k.set_value("", &"Edit with Simple Editor")?;
42        k.set_value("Icon", &format!("\"{exe}\",0"))?;
43        let (c, _) = hk.create_subkey(format!(r"{key}\command"))?;
44        c.set_value("", &format!("\"{exe}\" \"%1\""))?;
45    }
46    notify();
47    Ok(())
48}
49
50pub fn uninstall() -> std::io::Result<()> {
51    let hk = RegKey::predef(HKEY_CURRENT_USER);
52    let _ = hk.delete_subkey_all(LEGACY_KEY);
53    let mut r = Ok(());
54    for ext in VIDEO_EXTS {
55        match hk.delete_subkey_all(key(ext)) {
56            Err(e) if e.kind() != ErrorKind::NotFound => r = Err(e),
57            _ => {}
58        }
59    }
60    notify();
61    r
62}
63
64fn notify() {
65    use windows::Win32::UI::Shell::{SHChangeNotify, SHCNE_ASSOCCHANGED, SHCNF_IDLIST};
66    unsafe { SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, None, None) };
67}