Skip to content

Commit 96253fd

Browse files
authored
Merge pull request #169 from end2endzone/feature-issue167
Merge branch feature-issue167.
2 parents 98373af + 0ca7f27 commit 96253fd

16 files changed

+693
-57
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Changes for 0.10.0
88
* Fixed issue #158: Compilation fails on Github Action: `CPack error : Problem running WiX.`.
99
* Fixed issue #159: Unit test fails on Github Actions: TestPlugins.testServices().
1010
* Fixed issue #164: Fails to identify icon for HTML files.
11+
* Fixed issue #167: Improve the quality and accuracy of icon's fileextension attribute resolution (Icon::ResolveFileExtensionIcon()).
1112

1213

1314
Changes for 0.9.0

src/core/App.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,16 @@ namespace shellanything
137137
return mRandom;
138138
}
139139

140+
void App::SetIconResolutionService(IIconResolutionService* instance)
141+
{
142+
mIconResolutionService = instance;
143+
}
144+
145+
IIconResolutionService* App::GetIconResolutionService()
146+
{
147+
return mIconResolutionService;
148+
}
149+
140150
bool App::IsTestingEnvironment()
141151
{
142152
std::string process_path = ra::process::GetCurrentProcessPath();

src/core/App.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "IClipboardService.h"
3131
#include "IKeyboardService.h"
3232
#include "IRandomService.h"
33+
#include "IIconResolutionService.h"
3334

3435
#include <string>
3536

@@ -144,6 +145,21 @@ namespace shellanything
144145
/// <returns>Returns a pointer of the instance that is currently set. Returns NULL if no service is set.</returns>
145146
IRandomService* GetRandomService();
146147

148+
/// <summary>
149+
/// Set the current application icon resolution service.
150+
/// </summary>
151+
/// <remarks>
152+
/// If a service instance is already set, the caller must properly destroy the old instance.
153+
/// </remarks>
154+
/// <param name="instance">A valid instance of a the service.</param>
155+
void SetIconResolutionService(IIconResolutionService* instance);
156+
157+
/// <summary>
158+
/// Get the current application random service.
159+
/// </summary>
160+
/// <returns>Returns a pointer of the instance that is currently set. Returns NULL if no service is set.</returns>
161+
IIconResolutionService* GetIconResolutionService();
162+
147163
/// <summary>
148164
/// Test if application is loaded in a test environment (main's tests executable).
149165
/// </summary>
@@ -199,6 +215,7 @@ namespace shellanything
199215
IClipboardService* mClipboard;
200216
IKeyboardService* mKeyboard;
201217
IRandomService* mRandom;
218+
IIconResolutionService* mIconResolutionService;
202219
};
203220

204221

src/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ set(SHELLANYTHING_CORE_HEADER_FILES ""
1818
${CMAKE_SOURCE_DIR}/src/core/Environment.h
1919
${CMAKE_SOURCE_DIR}/src/core/Icon.h
2020
${CMAKE_SOURCE_DIR}/src/core/IObject.h
21+
${CMAKE_SOURCE_DIR}/src/core/IIconResolutionService.h
2122
${CMAKE_SOURCE_DIR}/src/core/IKeyboardService.h
2223
${CMAKE_SOURCE_DIR}/src/core/ILiveProperty.h
2324
${CMAKE_SOURCE_DIR}/src/core/IClipboardService.h
@@ -62,6 +63,7 @@ add_library(sa.core SHARED
6263
IAttributeValidator.cpp
6364
Icon.cpp
6465
IObject.cpp
66+
IIconResolutionService.cpp
6567
IKeyboardService.cpp
6668
ILiveProperty.cpp
6769
IClipboardService.cpp
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**********************************************************************************
2+
* MIT License
3+
*
4+
* Copyright (c) 2018 Antoine Beauchamp
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*********************************************************************************/
24+
25+
#include "IIconResolutionService.h"
26+
27+
namespace shellanything
28+
{
29+
30+
IIconResolutionService::IIconResolutionService()
31+
{
32+
}
33+
34+
IIconResolutionService::~IIconResolutionService()
35+
{
36+
}
37+
38+
} //namespace shellanything

src/core/IIconResolutionService.h

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**********************************************************************************
2+
* MIT License
3+
*
4+
* Copyright (c) 2018 Antoine Beauchamp
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*********************************************************************************/
24+
25+
#ifndef SA_IICON_RESOLUTION_SERVICE_H
26+
#define SA_IICON_RESOLUTION_SERVICE_H
27+
28+
#include "shellanything/export.h"
29+
#include "shellanything/config.h"
30+
#include "Icon.h"
31+
32+
#include <stdint.h>
33+
34+
namespace shellanything
35+
{
36+
/// <summary>
37+
/// Abstract icon resolution service class.
38+
/// Used to decouple the core from the operating system.
39+
/// </summary>
40+
class SHELLANYTHING_EXPORT IIconResolutionService
41+
{
42+
public:
43+
IIconResolutionService();
44+
virtual ~IIconResolutionService();
45+
46+
private:
47+
// Disable and copy constructor, dtor and copy operator
48+
IIconResolutionService(const IIconResolutionService&);
49+
IIconResolutionService& operator=(const IIconResolutionService&);
50+
public:
51+
52+
/// <summary>
53+
/// Resolve an icon's file extension to the system icon for this file extension.
54+
/// </summary>
55+
/// <param name="icon">The icon that have its fileextension's attribute set to resolve.</param>
56+
/// <returns>Returns true if the operation is successful. Returns false otherwise.</returns>
57+
virtual bool ResolveFileExtensionIcon(Icon & icon) = 0;
58+
59+
/// <summary>
60+
/// Check if the given file extension have resolved before.
61+
/// </summary>
62+
/// <param name="file_entension">The file extension to check.</param>
63+
/// <returns>Returns true if the file extension has previously resolve to a valid icon. Returns false otherwise.</returns>
64+
virtual bool HaveResolvedBefore(const std::string& file_extension) const = 0;
65+
66+
/// <summary>
67+
/// Check if the given file extension have previously failed to resolve.
68+
/// </summary>
69+
/// <param name="file_entension">The file extension to check.</param>
70+
/// <returns>Returns true if the file extension has previously failed to resolve. Returns false otherwise.</returns>
71+
virtual bool HaveFailedBefore(const std::string& file_extension) const = 0;
72+
73+
};
74+
75+
} //namespace shellanything
76+
77+
#endif //SA_IICON_RESOLUTION_SERVICE_H

src/core/Icon.cpp

Lines changed: 24 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,16 @@
2323
*********************************************************************************/
2424

2525
#include "Icon.h"
26-
#include "SelectionContext.h"
27-
#include "PropertyManager.h"
28-
#include "Win32Registry.h"
26+
#include "App.h"
2927
#include "LoggerHelper.h"
28+
#include "PropertyManager.h"
29+
#include "SelectionContext.h"
3030
#include "SaUtils.h"
3131

3232
#include "rapidassist/strings.h"
33-
#include "rapidassist/environment.h"
3433

3534
namespace shellanything
3635
{
37-
Icon::FileExtensionSet Icon::mUnresolvedFileExtensions;
38-
3936
Icon::Icon() :
4037
mIndex(0) // As per documentation, "If the index is not specified, the value 0 is used." See Issue #17, #150 and #155.
4138
{
@@ -89,56 +86,30 @@ namespace shellanything
8986

9087
void Icon::ResolveFileExtensionIcon()
9188
{
92-
//is this menu have a file extension ?
89+
IIconResolutionService* icon_resolution_service = App::GetInstance().GetIconResolutionService();
90+
if (icon_resolution_service == NULL)
91+
{
92+
SA_LOG(ERROR) << "No Icon Resolution service configured for resolving file extensions to icon.";
93+
return;
94+
}
95+
9396
shellanything::PropertyManager& pmgr = shellanything::PropertyManager::GetInstance();
9497
std::string file_extension = pmgr.Expand(mFileExtension);
95-
if (!file_extension.empty())
98+
99+
const bool have_resolved_before = icon_resolution_service->HaveResolvedBefore(file_extension);
100+
const bool have_failed_before = icon_resolution_service->HaveFailedBefore(file_extension);
101+
102+
//Do the actual resolution
103+
bool success = icon_resolution_service->ResolveFileExtensionIcon(*this);
104+
105+
// Print a success/failure message once. Issue #98.
106+
if (success && !have_resolved_before)
107+
{
108+
SA_LOG(INFO) << "Resolving icon for file extension '" << file_extension << "' to file '" << mPath << "' with index '" << mIndex << "'";
109+
}
110+
else if (!success && !have_failed_before)
96111
{
97-
//check for multiple values. keep the first value, forget about other selected file extensions.
98-
const std::string separator = pmgr.GetProperty(SelectionContext::MULTI_SELECTION_SEPARATOR_PROPERTY_NAME);
99-
if (file_extension.find(separator) != std::string::npos)
100-
{
101-
//multiple values detected.
102-
ra::strings::StringVector extension_list = ra::strings::Split(file_extension, separator.c_str());
103-
if (!extension_list.empty())
104-
file_extension = extension_list[0];
105-
}
106-
107-
//try to find the path to the icon module for the given file extension.
108-
Win32Registry::REGISTRY_ICON resolved_icon = Win32Registry::GetFileTypeIcon(file_extension.c_str());
109-
110-
//An icon with a negative index is valid from the registry.
111-
//Only the special case index = -1 should be considered invalid (Issue #17).
112-
//And ShellAnything accept positive (index) and negative index (resource id). (Issue #155, Issue #164).
113-
//See issue #17, 155, 164.
114-
if (Win32Registry::IsValid(resolved_icon))
115-
{
116-
//found the icon for the file extension
117-
//replace this menu's icon with the new information
118-
SA_LOG(INFO) << "Resolving icon for file extension '" << file_extension << "' to file '" << resolved_icon.path << "' with index '" << resolved_icon.index << "'";
119-
mPath = resolved_icon.path;
120-
mIndex = resolved_icon.index;
121-
mFileExtension = "";
122-
}
123-
else
124-
{
125-
//failed to find a valid icon.
126-
//using the default "unknown" icon
127-
Win32Registry::REGISTRY_ICON unknown_file_icon = Win32Registry::GetUnknownFileTypeIcon();
128-
mPath = unknown_file_icon.path;
129-
mIndex = unknown_file_icon.index;
130-
mFileExtension = "";
131-
132-
//show the message only once in logs
133-
const bool is_already_in_log = mUnresolvedFileExtensions.find(file_extension) != mUnresolvedFileExtensions.end();
134-
if (!is_already_in_log)
135-
{
136-
SA_LOG(WARNING) << "Failed to find icon for file extension '" << file_extension << "'. Resolving icon with default icon for unknown file type '" << unknown_file_icon.path << "' with index '" << unknown_file_icon.index << "'";
137-
138-
//remember this failure.
139-
mUnresolvedFileExtensions.insert(file_extension);
140-
}
141-
}
112+
SA_LOG(WARNING) << "Failed to resolve icon for file extension '" << file_extension << "'.";
142113
}
143114
}
144115

src/core/Icon.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,6 @@ namespace shellanything
119119
virtual void ToLongString(std::string& str, int indent) const;
120120

121121
private:
122-
typedef std::set<std::string /*file extension*/> FileExtensionSet;
123-
static FileExtensionSet mUnresolvedFileExtensions;
124122
std::string mFileExtension;
125123
std::string mPath;
126124
int mIndex;

src/shellextension/dllmain.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "WindowsClipboardService.h"
4040
#include "WindowsKeyboardService.h"
4141
#include "PcgRandomService.h"
42+
#include "WindowsIconResolutionService.h"
4243

4344
#include "shellanything/version.h"
4445
#include "shellanything/config.h"
@@ -55,6 +56,7 @@ shellanything::IRegistryService* registry_service = NULL;
5556
shellanything::IClipboardService* clipboard_service = NULL;
5657
shellanything::IKeyboardService* keyboard_service = NULL;
5758
shellanything::IRandomService* random_service = NULL;
59+
shellanything::IIconResolutionService* icon_resolution_service = NULL;
5860

5961
class CShellAnythingModule : public ATL::CAtlDllModuleT< CShellAnythingModule >
6062
{
@@ -198,6 +200,10 @@ extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpRe
198200
random_service = new shellanything::PcgRandomService();
199201
app.SetRandomService(random_service);
200202

203+
// Setup an active icon resolution service in ShellAnything's core.
204+
icon_resolution_service = new shellanything::WindowsIconResolutionService();
205+
app.SetIconResolutionService(icon_resolution_service);
206+
201207
// Setup and starting application
202208
app.Start();
203209

@@ -218,11 +224,13 @@ extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpRe
218224
delete clipboard_service;
219225
delete registry_service;
220226
delete logger_service;
227+
delete icon_resolution_service;
221228
random_service = NULL;
222229
keyboard_service = NULL;
223230
clipboard_service = NULL;
224231
registry_service = NULL;
225232
logger_service = NULL;
233+
icon_resolution_service = NULL;
226234
}
227235
}
228236

src/tests/TestIcon.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ namespace shellanything
240240
ASSERT_EQ(UNKNOWN_ICON_INDEX, icon.GetIndex());
241241

242242
//act (issue #98)
243-
for (int i = 0; i < 500; i++)
243+
for (int i = 0; i < 100; i++)
244244
{
245245
//this should create multiple log entries if the feature is not properly implemented.
246246
Icon tmp_icon;
@@ -263,7 +263,16 @@ namespace shellanything
263263
ASSERT_TRUE(read) << "Failed to read log file: " << path;
264264

265265
int count = CountString(content, UNKNOWN_FILE_EXTENSION);
266-
ASSERT_LE(count, 1) << "Log file '" << path << "' contains " << count << " references to '" << UNKNOWN_FILE_EXTENSION << "'.";
266+
267+
// Define how many reference we should see in log file.
268+
// With LegacyIconResolutionService class as active service, a maximum of 2 reference is allowed:
269+
// One from LegacyIconResolutionService class and another from Icon::ResolveFileExtensionIcon().
270+
//
271+
// With WindowsIconResolutionService class as active service, a maximum of 3 reference is allowed:
272+
// Two from WindowsIconResolutionService class and another from Icon::ResolveFileExtensionIcon().
273+
static const int MAX_COUNT = 3; // Issue #18. Issue #167.
274+
275+
ASSERT_LE(count, MAX_COUNT) << "Log file '" << path << "' contains " << count << " references to '" << UNKNOWN_FILE_EXTENSION << "'.";
267276
}
268277
}
269278
}

0 commit comments

Comments
 (0)