LeechCraft  0.6.70-10870-g558588d6ec
Modular cross-platform feature rich live environment.
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 <util/sll/util.h>
35 #include <qt_windows.h>
36 #include <shellapi.h>
37 
38 namespace LeechCraft
39 {
40 namespace Util
41 {
42  namespace
43  {
44  bool ReadValue (HKEY hKey, const QString& valueName, QString& result)
45  {
46  DWORD buffSize {};
47  if (ERROR_SUCCESS == RegQueryValueExW (hKey,
48  valueName.toStdWString ().c_str (),
49  nullptr,
50  nullptr,
51  nullptr,
52  &buffSize))
53  {
54  std::vector<wchar_t> buff (buffSize / sizeof (wchar_t));
55  if (ERROR_SUCCESS == RegQueryValueExW (hKey,
56  valueName.toStdWString ().c_str (),
57  nullptr,
58  nullptr,
59  reinterpret_cast<LPBYTE> (&buff [0]),
60  &buffSize))
61  {
62  result = QString::fromWCharArray (buff.data ());
63  return true;
64  }
65  }
66  return false;
67  }
68 
69  bool ReadDefaultValue (const QString& fullKey, QString& result)
70  {
71  DWORD buffSize {};
72  HKEY hKey {};
73  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
74  fullKey.toStdWString ().c_str (),
75  &hKey))
76  return false;
77 
78  const auto regGuard = Util::MakeScopeGuard ([&hKey] { RegCloseKey (hKey); });
79 
80  DWORD type { REG_SZ };
81 
82  if (ERROR_SUCCESS == RegQueryValueExW (hKey,
83  nullptr,
84  nullptr,
85  &type,
86  nullptr,
87  &buffSize))
88  {
89  std::vector<wchar_t> buff (buffSize / sizeof (wchar_t));
90  if (ERROR_SUCCESS == RegQueryValueExW (hKey,
91  nullptr,
92  nullptr,
93  &type,
94  reinterpret_cast<LPBYTE> (&buff [0]),
95  &buffSize))
96  {
97  result = QString::fromWCharArray (buff.data ());
98 
99  if (REG_EXPAND_SZ == type)
100  {
101  buffSize = ExpandEnvironmentStringsW (result.toStdWString ().c_str (), nullptr, 0);
102  buff.resize (buffSize);
103  if (!ExpandEnvironmentStringsW (result.toStdWString ().c_str (), &buff [0], buffSize))
104  {
105  return false;
106  }
107  result = QString::fromWCharArray (buff.data ());
108  }
109  return true;
110  }
111  }
112  return false;
113  }
114 
115  bool GetSubkeyNames (HKEY hKey, QList<QString>& result)
116  {
117  DWORD subKeys {};
118  DWORD maxSubkeyLen {};
119 
120  if (ERROR_SUCCESS != RegQueryInfoKeyW (hKey,
121  nullptr,
122  nullptr,
123  nullptr,
124  &subKeys,
125  &maxSubkeyLen,
126  nullptr,
127  nullptr,
128  nullptr,
129  nullptr,
130  nullptr,
131  nullptr))
132  {
133  qDebug () << Q_FUNC_INFO
134  << "RegQueryInfoKeyW failed.";
135  return false;
136  }
137 
138  result.clear ();
139  std::vector<wchar_t> keyName (maxSubkeyLen + 1);
140  for (DWORD i = 0; i < subKeys; ++i)
141  {
142  if (ERROR_SUCCESS != RegEnumKeyW (hKey,
143  i,
144  &keyName [0],
145  keyName.size ()
146  ))
147  break;
148 
149  result.append (QString::fromWCharArray (keyName.data ()));
150  }
151  return true;
152  }
153 
154  QHash<QString, QString> ParseMimeDatabase ()
155  {
156  HKEY hKey {};
157 
158  static const QString kDatabaseKey { "Mime\\Database\\Content Type" };
159  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
160  kDatabaseKey.toStdWString ().c_str (),
161  &hKey))
162  return {};
163 
164  QList<QString> mimes;
165  if (!GetSubkeyNames (hKey, mimes))
166  {
167  RegCloseKey (hKey);
168  return {};
169  }
170 
171  RegCloseKey (hKey);
172 
173  QHash<QString, QString> result;
174  for (const QString& mime : mimes)
175  {
176  static const QString kFormat { "Mime\\Database\\Content Type\\%1" };
177  static const QString kExtension { "Extension" };
178  static const QChar kDot {'.'};
179 
180  const QString fullKeyPath = kFormat.arg (mime);
181 
182  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
183  fullKeyPath.toStdWString ().c_str (),
184  &hKey))
185  continue;
186 
187  QString ext;
188  if (ReadValue (hKey, kExtension, ext) && !ext.isEmpty ())
189  {
190  if (ext.at (0) == kDot)
191  ext.remove (0, 1);
192  result.insert (ext, mime);
193  }
194 
195  RegCloseKey (hKey);
196  }
197 
198  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
199  nullptr,
200  &hKey))
201  return result;
202 
203  QList<QString> extensions;
204  if (!GetSubkeyNames (hKey, extensions))
205  {
206  RegCloseKey (hKey);
207  return result;
208  }
209  RegCloseKey (hKey);
210 
211  static const QString kValueContentType { "Content Type" };
212  for (int i = 0; i < extensions.count (); ++i)
213  {
214  static const auto prefix = QChar {'.'};
215  if (!extensions.at (i).startsWith (prefix))
216  continue;
217 
218  extensions [i].remove (0, 1);
219 
220  if (result.contains (extensions.at (i)))
221  continue;
222 
223  if (ERROR_SUCCESS != RegOpenKeyW (HKEY_CLASSES_ROOT,
224  QString { ".%1" }.arg (extensions.at (i)).toStdWString ().c_str (),
225  &hKey))
226  continue;
227 
228  QString mime;
229  if (ReadValue (hKey, kValueContentType, mime) && !mime.isEmpty ())
230  result.insert (extensions.at (i), mime);
231 
232  RegCloseKey (hKey);
233  }
234  return result;
235  }
236  }
237 
238  class ExtensionsDataImpl::Details
239  {
240  public:
241  const QHash<QString, QString> Extension2Mime_;
242  QMultiHash<QString, QString> Mime2Extension_;
243  QHash<QString, QIcon> Extension2Icon_;
244  QMutex IconsLock_;
245 
246  Details ();
247 
248  QIcon GetExtensionIcon (const QString& extension);
249  QString MimeByExtension (const QString& extension) const;
250  QString ExtensionByMime (const QString& mime) const;
251  };
252 
254  : Extension2Mime_ { ParseMimeDatabase () }
255  {
256  for (auto it = Extension2Mime_.constBegin (); it != Extension2Mime_.constEnd (); ++it)
257  Mime2Extension_.insertMulti (it.value (), it.key ());
258  }
259 
260  QString ExtensionsDataImpl::Details::MimeByExtension (const QString& extension) const
261  {
262  return Extension2Mime_.value (extension);
263  }
264 
265  QString ExtensionsDataImpl::Details::ExtensionByMime (const QString& mime) const
266  {
267  return Mime2Extension_.values (mime).first ();
268  }
269 
270  QIcon ExtensionsDataImpl::Details::GetExtensionIcon (const QString& extension)
271  {
272  QMutexLocker lock { &IconsLock_ };
273  if (Extension2Icon_.contains (extension))
274  return Extension2Icon_.value (extension);
275  lock.unlock ();
276 
277  static const QString kKeyDefaultIcon { "DefaultIcon" };
278  static const QChar kDot { '.' };
279  static const QChar kComma { ',' };
280 
281  const QString dottedExt = kDot + extension;
282  QString defIconKey = dottedExt + "\\" + kKeyDefaultIcon;
283 
284  QString defIconPath;
285  if (!ReadDefaultValue (defIconKey, defIconPath))
286  {
287  QString defaultType;
288  if (ReadDefaultValue (dottedExt, defaultType))
289  {
290  defIconKey = defaultType + "\\" + kKeyDefaultIcon;
291  ReadDefaultValue (defIconKey, defIconPath);
292  }
293  }
294 
295  if (defIconPath.isEmpty ())
296  return {};
297 
298  const QStringList parts = defIconPath.split (kComma);
299  if (2 != parts.count ())
300  return {};
301 
302  QString path = parts.at (0);
303  path.replace ("\"", {});
304 
305  const UINT index = parts.count () > 1 ? parts.at (1).toUInt () : 0;
306  const HICON hIcon = ExtractIconW (GetModuleHandle (nullptr), path.toStdWString ().c_str (), index);
307 
308  QIcon icon;
309  if (hIcon)
310  {
311  QPixmap pixmap { QPixmap::fromWinHICON (hIcon) };
312  if (!pixmap.isNull ())
313  icon.addPixmap (pixmap);
314  DestroyIcon (hIcon);
315  }
316  if (!icon.isNull ())
317  {
318  lock.relock ();
319  Extension2Icon_.insert (extension, icon);
320  lock.unlock ();
321  }
322  return icon;
323  }
324 
326  : Details_ { new Details }
327  {
328  }
329 
330  const QHash<QString, QString>& ExtensionsDataImpl::GetMimeDatabase () const
331  {
332  return Details_->Extension2Mime_;
333  }
334 
335  QIcon ExtensionsDataImpl::GetExtIcon (const QString& extension) const
336  {
337  const auto& lowerExtension = extension.toLower ();
338  return Details_->GetExtensionIcon (lowerExtension);
339  }
340 
341  QIcon ExtensionsDataImpl::GetMimeIcon (const QString& mime) const
342  {
343  const auto& ext = Details_->ExtensionByMime (mime);
344  if (ext.isEmpty ())
345  return {};
346 
347  return Details_->GetExtensionIcon (ext);
348  }
349 }
350 }
detail::ScopeGuard< F > MakeScopeGuard(const F &f)
Returns an object performing passed function on scope exit.
Definition: util.h:157
QString ExtensionByMime(const QString &mime) const
const QHash< QString, QString > & GetMimeDatabase() const
QString MimeByExtension(const QString &extension) const
QIcon GetExtIcon(const QString &extension) const
QIcon GetMimeIcon(const QString &mime) const