LeechCraft  %{LEECHCRAFT_VERSION}
Modular cross-platform feature rich live environment.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
extensionsdataimpl_w32.cpp
Go to the documentation of this file.
1 /**********************************************************************
2  * LeechCraft - modular cross-platform feature rich internet client.
3  * Copyright (C) 2006-2014 Georg Rudoy
4  *
5  * Boost Software License - Version 1.0 - August 17th, 2003
6  *
7  * Permission is hereby granted, free of charge, to any person or organization
8  * obtaining a copy of the software and accompanying documentation covered by
9  * this license (the "Software") to use, reproduce, display, distribute,
10  * execute, and transmit the Software, and to prepare derivative works of the
11  * Software, and to permit third-parties to whom the Software is furnished to
12  * do so, all subject to the following:
13  *
14  * The copyright notices in the Software and this entire statement, including
15  * the above license grant, this restriction and the following disclaimer,
16  * must be included in all copies of the Software, in whole or in part, and
17  * all derivative works of the Software, unless such copies or derivative
18  * works are solely in the form of machine-executable object code generated by
19  * a source language processor.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  **********************************************************************/
29 
30 #include "extensionsdataimpl.h"
31 #include <QIcon>
32 #include <QMutex>
33 #include <QtDebug>
34 #include <qt_windows.h>
35 #include <shellapi.h>
36 
37 namespace LeechCraft
38 {
39 namespace Util
40 {
41  namespace
42  {
43  bool ReadValue (HKEY hKey, const QString& valueName, QString& result)
44  {
45  DWORD buffSize {};
46  if (ERROR_SUCCESS == RegQueryValueExW (hKey,
47  valueName.toStdWString ().c_str (),
48  nullptr,
49  nullptr,
50  nullptr,
51  &buffSize))
52  {
53  std::vector<wchar_t> buff (buffSize / sizeof (wchar_t));
54  if (ERROR_SUCCESS == RegQueryValueExW (hKey,
55  valueName.toStdWString ().c_str (),
56  nullptr,
57  nullptr,
58  reinterpret_cast<LPBYTE> (&buff [0]),
59  &buffSize))
60  {
61  result = QString::fromWCharArray (buff.data ());
62  return true;
63  }
64  }
65  return false;
66  }
67 
68  bool ReadDefaultValue (const QString& fullKey, QString& result)
69  {
70  DWORD buffSize {};
71  HKEY hKey {};
72  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
73  fullKey.toStdWString ().c_str (),
74  &hKey))
75  return false;
76 
77  std::shared_ptr<void> regGuard (nullptr,
78  [&] (void*) { RegCloseKey (hKey); });
79  Q_UNUSED (regGuard);
80 
81  DWORD type { REG_SZ };
82 
83  if (ERROR_SUCCESS == RegQueryValueExW (hKey,
84  nullptr,
85  nullptr,
86  &type,
87  nullptr,
88  &buffSize))
89  {
90  std::vector<wchar_t> buff (buffSize / sizeof (wchar_t));
91  if (ERROR_SUCCESS == RegQueryValueExW (hKey,
92  nullptr,
93  nullptr,
94  &type,
95  reinterpret_cast<LPBYTE> (&buff [0]),
96  &buffSize))
97  {
98  result = QString::fromWCharArray (buff.data ());
99 
100  if (REG_EXPAND_SZ == type)
101  {
102  buffSize = ExpandEnvironmentStringsW (result.toStdWString ().c_str (), nullptr, 0);
103  buff.resize (buffSize);
104  if (!ExpandEnvironmentStringsW (result.toStdWString ().c_str (), &buff [0], buffSize))
105  {
106  return false;
107  }
108  result = QString::fromWCharArray (buff.data ());
109  }
110  return true;
111  }
112  }
113  return false;
114  }
115 
116  bool GetSubkeyNames (HKEY hKey, QList<QString>& result)
117  {
118  DWORD subKeys {};
119  DWORD maxSubkeyLen {};
120 
121  if (ERROR_SUCCESS != RegQueryInfoKeyW (hKey,
122  nullptr,
123  nullptr,
124  nullptr,
125  &subKeys,
126  &maxSubkeyLen,
127  nullptr,
128  nullptr,
129  nullptr,
130  nullptr,
131  nullptr,
132  nullptr))
133  {
134  qDebug () << Q_FUNC_INFO
135  << "RegQueryInfoKeyW failed.";
136  return false;
137  }
138 
139  result.clear ();
140  std::vector<wchar_t> keyName (maxSubkeyLen + 1);
141  for (DWORD i = 0; i < subKeys; ++i)
142  {
143  if (ERROR_SUCCESS != RegEnumKeyW (hKey,
144  i,
145  &keyName [0],
146  keyName.size ()
147  ))
148  break;
149 
150  result.append (QString::fromWCharArray (keyName.data ()));
151  }
152  return true;
153  }
154 
155  QHash<QString, QString> ParseMimeDatabase ()
156  {
157  HKEY hKey {};
158 
159  static const QString kDatabaseKey { "Mime\\Database\\Content Type" };
160  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
161  kDatabaseKey.toStdWString ().c_str (),
162  &hKey))
163  return {};
164 
165  QList<QString> mimes;
166  if (!GetSubkeyNames (hKey, mimes))
167  {
168  RegCloseKey (hKey);
169  return {};
170  }
171 
172  RegCloseKey (hKey);
173 
174  QHash<QString, QString> result;
175  for (const QString& mime : mimes)
176  {
177  static const QString kFormat { "Mime\\Database\\Content Type\\%1" };
178  static const QString kExtension { "Extension" };
179  static const QChar kDot {'.'};
180 
181  const QString fullKeyPath = kFormat.arg (mime);
182 
183  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
184  fullKeyPath.toStdWString ().c_str (),
185  &hKey))
186  continue;
187 
188  QString ext;
189  if (ReadValue (hKey, kExtension, ext) && !ext.isEmpty ())
190  {
191  if (ext.at (0) == kDot)
192  ext.remove (0, 1);
193  result.insert (ext, mime);
194  }
195 
196  RegCloseKey (hKey);
197  }
198 
199  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
200  nullptr,
201  &hKey))
202  return result;
203 
204  QList<QString> extensions;
205  if (!GetSubkeyNames (hKey, extensions))
206  {
207  RegCloseKey (hKey);
208  return result;
209  }
210  RegCloseKey (hKey);
211 
212  static const QString kValueContentType { "Content Type" };
213  for (int i = 0; i < extensions.count (); ++i)
214  {
215  static const auto prefix = QChar {'.'};
216  if (!extensions.at (i).startsWith (prefix))
217  continue;
218 
219  extensions [i].remove (0, 1);
220 
221  if (result.contains (extensions.at (i)))
222  continue;
223 
224  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
225  QString { ".%1" }.arg (extensions.at (i)).toStdWString ().c_str (),
226  &hKey))
227  continue;
228 
229  QString mime;
230  if (ReadValue (hKey, kValueContentType, mime) && !mime.isEmpty ())
231  result.insert (extensions.at (i), mime);
232 
233  RegCloseKey (hKey);
234  }
235  return result;
236  }
237  }
238 
239  class ExtensionsDataImpl::Details
240  {
241  public:
242  const QHash<QString, QString> Extension2Mime_;
243  QMultiHash<QString, QString> Mime2Extension_;
244  QHash<QString, QIcon> Extension2Icon_;
245  QMutex IconsLock_;
246 
247  Details ();
248 
249  QIcon GetExtensionIcon (const QString& extension);
250  QString MimeByExtension (const QString& extension) const;
251  QString ExtensionByMime (const QString& mime) const;
252  };
253 
255  : Extension2Mime_ { ParseMimeDatabase () }
256  {
257  for (auto it = Extension2Mime_.constBegin (); it != Extension2Mime_.constEnd (); ++it)
258  Mime2Extension_.insertMulti (it.value (), it.key ());
259  }
260 
261  QString ExtensionsDataImpl::Details::MimeByExtension (const QString& extension) const
262  {
263  return Extension2Mime_.value (extension);
264  }
265 
266  QString ExtensionsDataImpl::Details::ExtensionByMime (const QString& mime) const
267  {
268  return Mime2Extension_.values (mime).first ();
269  }
270 
271  QIcon ExtensionsDataImpl::Details::GetExtensionIcon (const QString& extension)
272  {
273  QMutexLocker lock { &IconsLock_ };
274  if (Extension2Icon_.contains (extension))
275  return Extension2Icon_.value (extension);
276  lock.unlock ();
277 
278  static const QString kKeyDefaultIcon { "DefaultIcon" };
279  static const QChar kDot { '.' };
280  static const QChar kComma { ',' };
281 
282  const QString dottedExt = kDot + extension;
283  QString defIconKey = dottedExt + "\\" + kKeyDefaultIcon;
284 
285  QString defIconPath;
286  if (!ReadDefaultValue (defIconKey, defIconPath))
287  {
288  QString defaultType;
289  if (ReadDefaultValue (dottedExt, defaultType))
290  {
291  defIconKey = defaultType + "\\" + kKeyDefaultIcon;
292  ReadDefaultValue (defIconKey, defIconPath);
293  }
294  }
295 
296  if (defIconPath.isEmpty ())
297  return {};
298 
299  const QStringList parts = defIconPath.split (kComma);
300  if (2 != parts.count ())
301  return {};
302 
303  QString path = parts.at (0);
304  path.replace ("\"", {});
305 
306  const UINT index = parts.count () > 1 ? parts.at (1).toUInt () : 0;
307  const HICON hIcon = ExtractIconW (GetModuleHandle (nullptr), path.toStdWString ().c_str (), index);
308 
309  QIcon icon;
310  if (hIcon)
311  {
312  QPixmap pixmap { QPixmap::fromWinHICON (hIcon) };
313  if (!pixmap.isNull ())
314  icon.addPixmap (pixmap);
315  DestroyIcon (hIcon);
316  }
317  if (!icon.isNull ())
318  {
319  lock.relock ();
320  Extension2Icon_.insert (extension, icon);
321  lock.unlock ();
322  }
323  return icon;
324  }
325 
327  : Details_ { new Details }
328  {
329  }
330 
331  const QHash<QString, QString>& ExtensionsDataImpl::GetMimeDatabase () const
332  {
333  return Details_->Extension2Mime_;
334  }
335 
336  QIcon ExtensionsDataImpl::GetExtIcon (const QString& extension) const
337  {
338  const auto& lowerExtension = extension.toLower ();
339  return Details_->GetExtensionIcon (lowerExtension);
340  }
341 
342  QIcon ExtensionsDataImpl::GetMimeIcon (const QString& mime) const
343  {
344  const auto& ext = Details_->ExtensionByMime (mime);
345  if (ext.isEmpty ())
346  return {};
347 
348  return Details_->GetExtensionIcon (ext);
349  }
350 }
351 }
const QHash< QString, QString > & GetMimeDatabase() const
QIcon GetExtIcon(const QString &extension) const
QIcon GetMimeIcon(const QString &mime) const
QString MimeByExtension(const QString &extension) const
QString ExtensionByMime(const QString &mime) const