OSDN Git Service

Bump version.
[lamexp/LameXP.git] / src / ShellIntegration.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2015 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "ShellIntegration.h"
24
25 //Internal
26 #include "Global.h"
27 #include "Registry_Decoder.h"
28
29 //MUtils
30 #include <MUtils/Global.h>
31 #include <MUtils/Exception.h>
32
33 //Qt
34 #include <QString>
35 #include <QStringList>
36 #include <QRegExp>
37 #include <QApplication>
38 #include <QFileInfo>
39 #include <QDir>
40 #include <QMutexLocker>
41 #include <QtConcurrentRun>
42
43 //Win32 API
44 #include <Shlobj.h>
45 #include <Shlwapi.h>
46
47 //Const
48 static const char *g_lamexpShellAction = "ConvertWithLameXP";
49 static const char *g_lamexpFileType = "LameXP.SupportedAudioFile";
50
51 //Mutex
52 QMutex ShellIntegration::m_mutex;
53
54 //Macros
55 #define REG_WRITE_STRING(KEY, STR) RegSetValueEx(key, NULL, NULL, REG_SZ, reinterpret_cast<const BYTE*>(STR.utf16()), (STR.size() + 1) * sizeof(wchar_t))
56
57 ////////////////////////////////////////////////////////////
58 // Constructor
59 ////////////////////////////////////////////////////////////
60
61 ShellIntegration::ShellIntegration(void)
62 {
63         MUTILS_THROW("Cannot create instance of this class, sorry!");
64 }
65
66 ////////////////////////////////////////////////////////////
67 // Public Functions
68 ////////////////////////////////////////////////////////////
69
70 void ShellIntegration::install(bool async)
71 {
72         //Install asynchronously
73         if(async)
74         {
75                 QFuture<void>(QtConcurrent::run(install, false));
76                 return;
77         }
78         
79         //Serialize
80         QMutexLocker lock(&m_mutex);
81
82         //Registry key
83         HKEY key = NULL;
84         
85         //Init some consts
86         const QString lamexpFileType(g_lamexpFileType);
87         const QString lamexpFileInfo(tr("Audio File supported by LameXP"));
88         const QString lamexpShellText(tr("Convert this file with LameXP v%1").arg(QString().sprintf("%d.%02d", lamexp_version_major(), lamexp_version_minor())));
89         const QString lamexpShellCommand = QString("\"%1\" \"--add=%2\"").arg(QDir::toNativeSeparators(QFileInfo(QApplication::applicationFilePath()).canonicalFilePath()), "%1");
90         const QString lamexpShellAction(g_lamexpShellAction);
91
92         //Register the LameXP file type
93         if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1").arg(lamexpFileType)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
94         {
95                 REG_WRITE_STRING(key, lamexpFileInfo);
96                 RegCloseKey(key);
97         }
98         if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell").arg(lamexpFileType)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
99         {
100                 REG_WRITE_STRING(key, lamexpShellAction);
101                 RegCloseKey(key);
102         }
103         if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2").arg(lamexpFileType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
104         {
105                 REG_WRITE_STRING(key, lamexpShellText);
106                 RegCloseKey(key);
107         }
108         if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2\\command").arg(lamexpFileType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
109         {
110                 REG_WRITE_STRING(key, lamexpShellCommand);
111                 RegCloseKey(key);
112         }
113
114         //Detect supported file types
115         QStringList *types = detectTypes(lamexpFileType, lamexpShellAction);
116
117         //Add LameXP shell action to all supported file types
118         while(types && (!types->isEmpty()))
119         {
120                 QString currentType = types->takeFirst();
121
122                 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2").arg(currentType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
123                 {
124                         REG_WRITE_STRING(key, lamexpShellText);
125                         RegCloseKey(key);
126                 }
127
128                 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2\\command").arg(currentType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
129                 {
130                         REG_WRITE_STRING(key, lamexpShellCommand);
131                         RegCloseKey(key);
132                 }
133         }
134         
135         //Shell notification
136         SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
137
138         //Free
139         delete types;
140 }
141
142 void ShellIntegration::remove(bool async)
143 {
144         //Remove asynchronously
145         if(async)
146         {
147                 QFuture<void>(QtConcurrent::run(remove, false));
148                 return;
149         }
150
151         //Serialize
152         QMutexLocker lock(&m_mutex);
153         
154         //Init some consts
155         const QString lamexpFileType(g_lamexpFileType);
156         const QString lamexpShellAction(g_lamexpShellAction);
157
158         //Initialization
159         HKEY key = NULL;
160         QStringList fileTypes;
161
162         //Find all registered file types
163         if(RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Classes", NULL, KEY_ENUMERATE_SUB_KEYS ,&key) == ERROR_SUCCESS)
164         {
165                 wchar_t name[256];
166                 
167                 for(DWORD i = 0; true; i++)
168                 {
169                         DWORD size = 256;
170                         if(RegEnumKeyEx(key, i, name, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
171                         {
172                                 fileTypes << QString::fromUtf16(reinterpret_cast<const unsigned short*>(name));
173                                 continue;
174                         }
175                         break;
176                 }
177         }
178
179         //Remove shell action from all file types
180         while(!fileTypes.isEmpty())
181         {
182                 SHDeleteKey(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2").arg(fileTypes.takeFirst(), lamexpShellAction)));
183         }
184
185         //Unregister LameXP file type
186         SHDeleteKey(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1").arg(lamexpFileType)));
187
188         //Shell notification
189         SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
190 }
191
192 ////////////////////////////////////////////////////////////
193 // Private Functions
194 ////////////////////////////////////////////////////////////
195
196 QStringList *ShellIntegration::detectTypes(const QString &lamexpFileType, const QString &lamexpShellAction)
197 {
198         HKEY key = NULL;
199
200         QStringList *nativeTypes = new QStringList();
201         QStringList supportedTypes = DecoderRegistry::getSupportedTypes();
202         QStringList extensions;
203
204         QRegExp regExp1("\\((.+)\\)");
205         QRegExp regExp2("(\\.\\w+)");
206
207         //Find all supported file extensions
208         while(!supportedTypes.isEmpty())
209         {
210                 if(regExp1.lastIndexIn(supportedTypes.takeFirst()) > 0)
211                 {
212                         int lastIndex = 0;
213                         while((lastIndex = regExp2.indexIn(regExp1.cap(1), lastIndex) + 1) >= 1)
214                         {
215                                 extensions.append(regExp2.cap(1));
216                         }
217                 }
218         }
219
220         //Map supported extensions to native types
221         while(!extensions.isEmpty())
222         {
223                 QString currentExt = extensions.takeFirst();
224
225                 if(RegOpenKeyEx(HKEY_CLASSES_ROOT, MUTILS_WCHR(currentExt), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
226                 {
227                         wchar_t data[256];
228                         DWORD dataLen = 256 * sizeof(wchar_t);
229                         DWORD type = NULL;
230                                 
231                         if(RegQueryValueEx(key, NULL, NULL, &type, reinterpret_cast<BYTE*>(data), &dataLen) == ERROR_SUCCESS)
232                         {
233                                 if((type == REG_SZ) || (type == REG_EXPAND_SZ))
234                                 {
235                                         QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(data));
236                                         if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
237                                         {
238                                                 nativeTypes->append(currentType);
239                                         }
240                                 }
241                         }
242                         RegCloseKey(key);
243                 }
244                 else
245                 {
246                         if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1").arg(currentExt)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
247                         {
248                                 REG_WRITE_STRING(key, lamexpFileType);
249                                 RegCloseKey(key);
250                         }
251                 }
252
253                 if(RegOpenKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%1\\UserChoice").arg(currentExt)), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
254                 {
255                         wchar_t data[256];
256                         DWORD dataLen = 256 * sizeof(wchar_t);
257                         DWORD type = NULL;
258                                 
259                         if(RegQueryValueEx(key, L"Progid", NULL, &type, reinterpret_cast<BYTE*>(data), &dataLen) == ERROR_SUCCESS)
260                         {
261                                 if((type == REG_SZ) || (type == REG_EXPAND_SZ))
262                                 {
263                                         QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(data));
264                                         if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
265                                         {
266                                                 nativeTypes->append(currentType);
267                                         }
268                                 }
269                         }
270                         RegCloseKey(key);
271                 }
272
273                 if(RegOpenKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%1\\OpenWithProgids").arg(currentExt)), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
274                 {
275                         wchar_t name[256];
276                         for(DWORD i = 0; true; i++)
277                         {
278                                 DWORD size = 256;
279                                 if(RegEnumValue(key, i, name, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
280                                 {
281                                         QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(name));
282                                         if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
283                                         {
284                                                 nativeTypes->append(currentType);
285                                         }
286                                         continue;
287                                 }
288                                 break;
289                         }
290                         RegCloseKey(key);
291                 }
292         }
293
294         return nativeTypes;
295 }