diff --git a/wfassoc-cdylib/cbindgen/wfassoc++.h b/wfassoc-cdylib/cbindgen/wfassoc++.h index f6842c7..0caa545 100644 --- a/wfassoc-cdylib/cbindgen/wfassoc++.h +++ b/wfassoc-cdylib/cbindgen/wfassoc++.h @@ -230,22 +230,15 @@ public: return *this; } - std::optional ResolveName() { + std::string ResolveName() { const char* name = nullptr; _Check(wfassoc::WFProgramResolveName(_token, &name)); - if (name == nullptr) { - return std::nullopt; - } else { - return std::string(name); - } + return std::string(name); } - std::optional ResolveIcon() { + IconRc ResolveIcon() { Token token = _INVALID_TOKEN(); _Check(wfassoc::WFProgramResolveIcon(_token, &token)); - if (token == _INVALID_TOKEN()) { - return std::nullopt; - } return IconRc(token); } diff --git a/wfassoc-cdylib/cbindgen/wfassoc.h b/wfassoc-cdylib/cbindgen/wfassoc.h index 24de69c..b56bde9 100644 --- a/wfassoc-cdylib/cbindgen/wfassoc.h +++ b/wfassoc-cdylib/cbindgen/wfassoc.h @@ -349,9 +349,14 @@ bool WFProgramDestroy(Token in_program); /** * @brief Resolve the provided program name of this Program + * + * The name will be user specified first, + * then fallback to program manifest file specified name, + * and finally fallback to the file name of executable. * * @param[in] in_program Program token - * @param[out] out_name Pointer to receive the resolved name, or NULL if not found. + * @param[out] out_name Pointer to receive the resolved name. + * There is no possibility that this value is NULL. * This string will be freed at the next API call. Please make a copy immediately if you need to use it longer. * @return true on success, false on failure */ @@ -359,9 +364,13 @@ bool WFProgramResolveName(Token in_program, CStyleString *out_name); /** * @brief Resolve the Program icon resource + * + * The icon will be user specified first, + * the fallback to the first icon of program, + * and finally fallback to the system default executable icon. * * @param[in] in_program Program token - * @param[out] out_icon_rc Pointer to receive the icon resource token, or invalid token if not found. + * @param[out] out_icon_rc Pointer to receive the icon resource token. * The caller take the ownership of created icon resource object. * And it should be freed by calling WFIconRcDestroy() when it is no longer needed. * @return true on success, false on failure @@ -471,7 +480,10 @@ bool WFProgramQueryExt(Token in_program, View in_view, size_t in_index, Token *o bool WFExtStatusDestroy(Token in_ext_status); /** - * @brief Get the name from an extension status object + * @brief Get the display name from an extension status object + * + * The display will be user specified first, + * the fallback to its ProgId verbatim. * * @param[in] in_ext_status Extension status token * @param[out] out_name Pointer to receive the name. @@ -485,8 +497,11 @@ bool WFExtStatusGetName(Token in_ext_status, CStyleString *out_name); /** * @brief Get the icon from an extension status object * + * The icon will be user specified first, + * the fallback to the system default file icon. + * * @param[in] in_ext_status Extension status token - * @param[out] out_icon Pointer to receive the icon handle, or INVALID_HICON if not available. + * @param[out] out_icon Pointer to receive the icon handle. * This icon handle will be freed once this icon resource object is destroyed. * Please make a copy immediately if you need to use it longer. * @return true on success, false on failure @@ -503,6 +518,9 @@ bool WFSelfExtStatusDestroy(Token in_self_ext_status); /** * @brief Get the display name from a self extension status object + * + * The display will be user specified first, + * the fallback to its ProgId verbatim. * * @param[in] in_self_ext_status Self extension status token * @param[out] out_name Pointer to receive the name string. @@ -514,9 +532,12 @@ bool WFSelfExtStatusGetName(Token in_self_ext_status, CStyleString *out_name); /** * @brief Get the icon from a self extension status object + * + * The icon will be user specified first, + * the fallback to the system default file icon. * * @param[in] in_self_ext_status Self extension status token - * @param[out] out_icon Pointer to receive the icon handle, or INVALID_HICON if not available. + * @param[out] out_icon Pointer to receive the icon handle. * This icon handle will be freed once this self extension status object is destroyed. * Please make a copy immediately if you need to use it longer. * @return true on success, false on failure diff --git a/wfassoc-cdylib/src/lib.rs b/wfassoc-cdylib/src/lib.rs index 7336569..0073e9c 100644 --- a/wfassoc-cdylib/src/lib.rs +++ b/wfassoc-cdylib/src/lib.rs @@ -492,16 +492,9 @@ pub extern "C" fn WFProgramResolveName( let mut pool = pull_writer!(PROGRAM_POOL)?; let program = pool.get_mut(in_program)?; - let name = match program.resolve_name()? { - Some(name) => { - cstr_ffi::set_ffi_string(&name)?; - cstr_ffi::get_ffi_string() - }, - None => { - std::ptr::null() - }, - }; - Ok(name) + let name = program.resolve_name()?; + cstr_ffi::set_ffi_string(&name)?; + Ok(cstr_ffi::get_ffi_string()) }) } @@ -515,16 +508,8 @@ pub extern "C" fn WFProgramResolveIcon( let program = pool.get_mut(in_program)?; let icon = program.resolve_icon()?; - let token = match icon { - Some(icon) => { - let mut pool = pull_writer!(ICON_RC_POOL)?; - pool.allocate(icon)? - }, - None => { - object_pool::invalid_token() - }, - }; - Ok(token) + let mut pool = pull_writer!(ICON_RC_POOL)?; + Ok(pool.allocate(icon)?) }) } @@ -707,11 +692,8 @@ pub extern "C" fn WFExtStatusGetIcon( let pool = pull_reader!(EXT_STATUS_POOL)?; let ext_status = pool.get(in_ext_status)?; - let icon = match ext_status.get_icon() { - Some(icon) => icon.get_icon(), - None => ffi_types::INVALID_HICON, - }; - Ok(icon) + let icon = ext_status.get_icon(); + Ok(icon.get_icon()) }) } @@ -750,11 +732,8 @@ pub extern "C" fn WFSelfExtStatusGetIcon( let pool = pull_reader!(SELF_EXT_STATUS_POOL)?; let self_ext_status = pool.get(in_self_ext_status)?; - let icon = match self_ext_status.get_icon() { - Some(icon) => icon.get_icon(), - None => ffi_types::INVALID_HICON, - }; - Ok(icon) + let icon = self_ext_status.get_icon(); + Ok(icon.get_icon()) }) } diff --git a/wfassoc/src/highlevel/program.rs b/wfassoc/src/highlevel/program.rs index 7cd3aa4..4fd0048 100644 --- a/wfassoc/src/highlevel/program.rs +++ b/wfassoc/src/highlevel/program.rs @@ -44,6 +44,8 @@ pub enum ParseProgramError { pub enum ProgramError { #[error("{0}")] Lowlevel(#[from] lowlevel::Error), + #[error("{0}")] + LoadIconRc(#[from] concept::LoadIconRcError), #[error("given index is invalid")] BadIndex, } @@ -57,6 +59,7 @@ pub struct Program { app_paths_key: lowlevel::AppPathsKey, applications_key: lowlevel::ApplicationsKey, app_path: String, + app_file_name: String, app_dir_path: String, name: Option>, icon: Option>, @@ -254,6 +257,7 @@ impl Program { app_paths_key, applications_key, app_path, + app_file_name, app_dir_path, name, icon, @@ -268,26 +272,43 @@ impl Program { } impl Program { - pub fn resolve_name(&self) -> Result, ProgramError> { - // TODO: - // Add fallback: fetch it from executable manifest file. - // Add fallback: use executable name directly. - Ok(self + pub fn resolve_name(&self) -> Result { + // Fecch from user specified name first + let name = self .name .as_ref() .map(|name| name.inner.extract().ok()) - .flatten()) + .flatten(); + if let Some(name) = name { + return Ok(name); + } + + // TODO: + // Fetch it from executable manifest file. + + // Finally fallback to use executable name. + Ok(self.app_file_name.clone()) } - pub fn resolve_icon(&self) -> Result, ProgramError> { - // TODO: - // Add fallback: fetch it from the first icon of executable. - // Add fallback: use system default executable icon. - Ok(self + pub fn resolve_icon(&self) -> Result { + // Fetch from user specified icon first + let icon = self .icon .as_ref() .map(|icon| icon.inner.extract(concept::IconSizeKind::Small).ok()) - .flatten()) + .flatten(); + if let Some(icon) = icon { + return Ok(icon); + } + // Fetch from the first icon of executable instead. + let icon = concept::IconRc::new(&self.app_path, 0, concept::IconSizeKind::Small).ok(); + if let Some(icon) = icon { + return Ok(icon); + } + // Finally fallback to use system default executable icon. + Ok(concept::IconRc::GENERIC_APPLICATION( + concept::IconSizeKind::Small, + )?) } pub fn exts_len(&self) -> usize { @@ -309,12 +330,14 @@ impl Program { .inner .extract() .unwrap_or(progid_ext_key.progid_key.inner().to_string()); - // Try to fetch icon + // Try to fetch icon, and fallback to system default file icon. let icon = progid_ext_key .icon .inner .extract(concept::IconSizeKind::Small) - .ok(); + .unwrap_or(concept::IconRc::GENERIC_DOCUMENT( + concept::IconSizeKind::Small, + )?); // Okey, return it Ok(ProgramSelfExtStatus::new( @@ -557,11 +580,14 @@ impl Program { .flatten(); } let name = name.unwrap_or(progid_key.inner().to_string()); - // Now try to fetch icon. + // Now try to fetch icon and fallback to system default file icon. let icon = progid_key .get_default_icon(view)? .map(|ico| ico.extract(concept::IconSizeKind::Small).ok()) - .flatten(); + .flatten() + .unwrap_or(concept::IconRc::GENERIC_DOCUMENT( + concept::IconSizeKind::Small, + )?); // Okey, return it. Ok(Some(ProgramExtStatus::new(name, icon))) @@ -617,11 +643,11 @@ struct ProgramProgIdExtKey { pub struct ProgramSelfExtStatus { ext: concept::Ext, name: String, - icon: Option, + icon: concept::IconRc, } impl ProgramSelfExtStatus { - fn new(ext: concept::Ext, name: String, icon: Option) -> Self { + fn new(ext: concept::Ext, name: String, icon: concept::IconRc) -> Self { Self { ext, name, icon } } @@ -646,8 +672,8 @@ impl ProgramSelfExtStatus { /// Get the icon of this program. /// /// Due to the icon is optional, if there is no icon, return None. - pub fn get_icon(&self) -> Option<&concept::IconRc> { - self.icon.as_ref() + pub fn get_icon(&self) -> &concept::IconRc { + &self.icon } } @@ -656,11 +682,11 @@ impl ProgramSelfExtStatus { /// The data including the diaplay name and icon. pub struct ProgramExtStatus { name: String, - icon: Option, + icon: concept::IconRc, } impl ProgramExtStatus { - fn new(name: String, icon: Option) -> Self { + fn new(name: String, icon: concept::IconRc) -> Self { Self { name, icon } } @@ -675,8 +701,8 @@ impl ProgramExtStatus { /// Get the icon of this program. /// /// Due to the icon is optional, if there is no icon, return None. - pub fn get_icon(&self) -> Option<&concept::IconRc> { - self.icon.as_ref() + pub fn get_icon(&self) -> &concept::IconRc { + &self.icon } } diff --git a/wfassoc/src/win32/concept.rs b/wfassoc/src/win32/concept.rs index ba9ecea..64e960f 100644 --- a/wfassoc/src/win32/concept.rs +++ b/wfassoc/src/win32/concept.rs @@ -510,6 +510,20 @@ pub struct IconRc { icon: HICON, } +impl IconRc { + /// Get the icon preset representing generic document. + #[allow(non_snake_case)] + pub fn GENERIC_DOCUMENT(kind: IconSizeKind) -> Result { + Self::new("shell32.dll", 0, kind) + } + + /// Get the icon preset representing generic executable. + #[allow(non_snake_case)] + pub fn GENERIC_APPLICATION(kind: IconSizeKind) -> Result { + Self::new("imageres.dll", 0, kind) + } +} + impl IconRc { /// Load icon from executable or `.ico` file. /// @@ -867,16 +881,19 @@ pub struct Verb { } impl Verb { + /// Get the verb representing Open. #[allow(non_snake_case)] pub fn OPEN() -> Self { Verb::new("open").expect("unexpected bad verb") } + /// Get the verb representing Edit. #[allow(non_snake_case)] pub fn EDIT() -> Self { Verb::new("edit").expect("unexpected bad verb") } + /// Get the verb representing Play. #[allow(non_snake_case)] pub fn PLAY() -> Self { Verb::new("play").expect("unexpected bad verb")