mimetype.cpp/.h split into unix,mac,msw
[wxWidgets.git] / src / msw / mimetype.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/mimetype.cpp
3 // Purpose: classes and functions to manage MIME types
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 23.09.98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "mimetype.h"
14 #endif
15
16 // for compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/defs.h"
25 #endif
26
27 #if (wxUSE_FILE && wxUSE_TEXTFILE) || defined(__WXMSW__)
28
29 #ifndef WX_PRECOMP
30 #include "wx/string.h"
31 #if wxUSE_GUI
32 #include "wx/icon.h"
33 #endif
34 #endif //WX_PRECOMP
35
36 // Doesn't compile in WIN16 mode
37 #ifndef __WIN16__
38
39 #include "wx/log.h"
40 #include "wx/file.h"
41 #include "wx/intl.h"
42 #include "wx/dynarray.h"
43 #include "wx/confbase.h"
44
45 #ifdef __WXMSW__
46 #include "wx/msw/registry.h"
47 #include "windows.h"
48 #elif defined(__UNIX__) || defined(__WXPM__)
49 #include "wx/ffile.h"
50 #include "wx/textfile.h"
51 #include "wx/dir.h"
52 #include "wx/utils.h"
53 #include "wx/tokenzr.h"
54 #endif // OS
55
56 #include "wx/msw/mimetype.h"
57
58 // other standard headers
59 #include <ctype.h>
60
61 // in case we're compiling in non-GUI mode
62 class WXDLLEXPORT wxIcon;
63
64
65 // These classes use Windows registry to retrieve the required information.
66 //
67 // Keys used (not all of them are documented, so it might actually stop working
68 // in futur versions of Windows...):
69 // 1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME
70 // types, each key has a string value "Extension" which gives (dot preceded)
71 // extension for the files of this MIME type.
72 //
73 // 2. "HKCR\.ext" contains
74 // a) unnamed value containing the "filetype"
75 // b) value "Content Type" containing the MIME type
76 //
77 // 3. "HKCR\filetype" contains
78 // a) unnamed value containing the description
79 // b) subkey "DefaultIcon" with single unnamed value giving the icon index in
80 // an icon file
81 // c) shell\open\command and shell\open\print subkeys containing the commands
82 // to open/print the file (the positional parameters are introduced by %1,
83 // %2, ... in these strings, we change them to %s ourselves)
84
85 // although I don't know of any official documentation which mentions this
86 // location, uses it, so it isn't likely to change
87 static const wxChar *MIME_DATABASE_KEY = wxT("MIME\\Database\\Content Type\\");
88
89
90
91
92 wxString wxFileTypeImpl::GetCommand(const wxChar *verb) const
93 {
94 // suppress possible error messages
95 wxLogNull nolog;
96 wxString strKey;
97
98 if ( wxRegKey(wxRegKey::HKCR, m_ext + _T("\\shell")).Exists() )
99 strKey = m_ext;
100 if ( wxRegKey(wxRegKey::HKCR, m_strFileType + _T("\\shell")).Exists() )
101 strKey = m_strFileType;
102
103 if ( !strKey )
104 {
105 // no info
106 return wxEmptyString;
107 }
108
109 strKey << wxT("\\shell\\") << verb;
110 wxRegKey key(wxRegKey::HKCR, strKey + _T("\\command"));
111 wxString command;
112 if ( key.Open() ) {
113 // it's the default value of the key
114 if ( key.QueryValue(wxT(""), command) ) {
115 // transform it from '%1' to '%s' style format string (now also
116 // test for %L - apparently MS started using it as well for the
117 // same purpose)
118
119 // NB: we don't make any attempt to verify that the string is valid,
120 // i.e. doesn't contain %2, or second %1 or .... But we do make
121 // sure that we return a string with _exactly_ one '%s'!
122 bool foundFilename = FALSE;
123 size_t len = command.Len();
124 for ( size_t n = 0; (n < len) && !foundFilename; n++ ) {
125 if ( command[n] == wxT('%') &&
126 (n + 1 < len) &&
127 (command[n + 1] == wxT('1') ||
128 command[n + 1] == wxT('L')) ) {
129 // replace it with '%s'
130 command[n + 1] = wxT('s');
131
132 foundFilename = TRUE;
133 }
134 }
135
136 // look whether we must issue some DDE requests to the application
137 // (and not just launch it)
138 strKey += _T("\\DDEExec");
139 wxRegKey keyDDE(wxRegKey::HKCR, strKey);
140 if ( keyDDE.Open() ) {
141 wxString ddeCommand, ddeServer, ddeTopic;
142 keyDDE.QueryValue(_T(""), ddeCommand);
143 ddeCommand.Replace(_T("%1"), _T("%s"));
144
145 wxRegKey(wxRegKey::HKCR, strKey + _T("\\Application")).
146 QueryValue(_T(""), ddeServer);
147 wxRegKey(wxRegKey::HKCR, strKey + _T("\\Topic")).
148 QueryValue(_T(""), ddeTopic);
149
150 // HACK: we use a special feature of wxExecute which exists
151 // just because we need it here: it will establish DDE
152 // conversation with the program it just launched
153 command.Prepend(_T("WX_DDE#"));
154 command << _T('#') << ddeServer
155 << _T('#') << ddeTopic
156 << _T('#') << ddeCommand;
157 }
158 else if ( !foundFilename ) {
159 // we didn't find any '%1' - the application doesn't know which
160 // file to open (note that we only do it if there is no DDEExec
161 // subkey)
162 //
163 // HACK: append the filename at the end, hope that it will do
164 command << wxT(" %s");
165 }
166 }
167 }
168 //else: no such file type or no value, will return empty string
169
170 return command;
171 }
172
173 bool
174 wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
175 const wxFileType::MessageParameters& params)
176 const
177 {
178 wxString cmd;
179 if ( m_info ) {
180 cmd = m_info->GetOpenCommand();
181 }
182 else {
183 cmd = GetCommand(wxT("open"));
184 }
185
186 *openCmd = wxFileType::ExpandCommand(cmd, params);
187
188 return !openCmd->IsEmpty();
189 }
190
191 bool
192 wxFileTypeImpl::GetPrintCommand(wxString *printCmd,
193 const wxFileType::MessageParameters& params)
194 const
195 {
196 wxString cmd;
197 if ( m_info ) {
198 cmd = m_info->GetPrintCommand();
199 }
200 else {
201 cmd = GetCommand(wxT("print"));
202 }
203
204 *printCmd = wxFileType::ExpandCommand(cmd, params);
205
206 return !printCmd->IsEmpty();
207 }
208
209 // TODO this function is half implemented
210 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
211 {
212 if ( m_info ) {
213 extensions = m_info->GetExtensions();
214
215 return TRUE;
216 }
217 else if ( m_ext.IsEmpty() ) {
218 // the only way to get the list of extensions from the file type is to
219 // scan through all extensions in the registry - too slow...
220 return FALSE;
221 }
222 else {
223 extensions.Empty();
224 extensions.Add(m_ext);
225
226 // it's a lie too, we don't return _all_ extensions...
227 return TRUE;
228 }
229 }
230
231 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
232 {
233 if ( m_info ) {
234 // we already have it
235 *mimeType = m_info->GetMimeType();
236
237 return TRUE;
238 }
239
240 // suppress possible error messages
241 wxLogNull nolog;
242 wxRegKey key(wxRegKey::HKCR, wxT(".") + m_ext);
243 if ( key.Open() && key.QueryValue(wxT("Content Type"), *mimeType) ) {
244 return TRUE;
245 }
246 else {
247 return FALSE;
248 }
249 }
250
251 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
252 {
253 #if wxUSE_GUI
254 if ( m_info ) {
255 // we don't have icons in the fallback resources
256 return FALSE;
257 }
258
259 wxString strIconKey;
260 strIconKey << m_strFileType << wxT("\\DefaultIcon");
261
262 // suppress possible error messages
263 wxLogNull nolog;
264 wxRegKey key(wxRegKey::HKCR, strIconKey);
265
266 if ( key.Open() ) {
267 wxString strIcon;
268 // it's the default value of the key
269 if ( key.QueryValue(wxT(""), strIcon) ) {
270 // the format is the following: <full path to file>, <icon index>
271 // NB: icon index may be negative as well as positive and the full
272 // path may contain the environment variables inside '%'
273 wxString strFullPath = strIcon.BeforeLast(wxT(',')),
274 strIndex = strIcon.AfterLast(wxT(','));
275
276 // index may be omitted, in which case BeforeLast(',') is empty and
277 // AfterLast(',') is the whole string
278 if ( strFullPath.IsEmpty() ) {
279 strFullPath = strIndex;
280 strIndex = wxT("0");
281 }
282
283 wxString strExpPath = wxExpandEnvVars(strFullPath);
284 int nIndex = wxAtoi(strIndex);
285
286 HICON hIcon = ExtractIcon(GetModuleHandle(NULL), strExpPath, nIndex);
287 switch ( (int)hIcon ) {
288 case 0: // means no icons were found
289 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
290 wxLogDebug(wxT("incorrect registry entry '%s': no such icon."),
291 key.GetName().c_str());
292 break;
293
294 default:
295 icon->SetHICON((WXHICON)hIcon);
296 return TRUE;
297 }
298 }
299 }
300
301 // no such file type or no value or incorrect icon entry
302 #endif // wxUSE_GUI
303
304 return FALSE;
305 }
306
307 bool wxFileTypeImpl::GetDescription(wxString *desc) const
308 {
309 if ( m_info ) {
310 // we already have it
311 *desc = m_info->GetDescription();
312
313 return TRUE;
314 }
315
316 // suppress possible error messages
317 wxLogNull nolog;
318 wxRegKey key(wxRegKey::HKCR, m_strFileType);
319
320 if ( key.Open() ) {
321 // it's the default value of the key
322 if ( key.QueryValue(wxT(""), *desc) ) {
323 return TRUE;
324 }
325 }
326
327 return FALSE;
328 }
329
330 // extension -> file type
331 wxFileType *
332 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
333 {
334 // add the leading point if necessary
335 wxString str;
336 if ( ext[0u] != wxT('.') ) {
337 str = wxT('.');
338 }
339 str << ext;
340
341 // suppress possible error messages
342 wxLogNull nolog;
343
344 bool knownExtension = FALSE;
345
346 wxString strFileType;
347 wxRegKey key(wxRegKey::HKCR, str);
348 if ( key.Open() ) {
349 // it's the default value of the key
350 if ( key.QueryValue(wxT(""), strFileType) ) {
351 // create the new wxFileType object
352 wxFileType *fileType = new wxFileType;
353 fileType->m_impl->Init(strFileType, ext);
354
355 return fileType;
356 }
357 else {
358 // this extension doesn't have a filetype, but it's known to the
359 // system and may be has some other useful keys (open command or
360 // content-type), so still return a file type object for it
361 knownExtension = TRUE;
362 }
363 }
364
365 // check the fallbacks
366 // TODO linear search is potentially slow, perhaps we should use a sorted
367 // array?
368 size_t count = m_fallbacks.GetCount();
369 for ( size_t n = 0; n < count; n++ ) {
370 if ( m_fallbacks[n].GetExtensions().Index(ext) != wxNOT_FOUND ) {
371 wxFileType *fileType = new wxFileType;
372 fileType->m_impl->Init(m_fallbacks[n]);
373
374 return fileType;
375 }
376 }
377
378 if ( knownExtension )
379 {
380 wxFileType *fileType = new wxFileType;
381 fileType->m_impl->Init(wxEmptyString, ext);
382
383 return fileType;
384 }
385 else
386 {
387 // unknown extension
388 return NULL;
389 }
390 }
391
392 // MIME type -> extension -> file type
393 wxFileType *
394 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
395 {
396 wxString strKey = MIME_DATABASE_KEY;
397 strKey << mimeType;
398
399 // suppress possible error messages
400 wxLogNull nolog;
401
402 wxString ext;
403 wxRegKey key(wxRegKey::HKCR, strKey);
404 if ( key.Open() ) {
405 if ( key.QueryValue(wxT("Extension"), ext) ) {
406 return GetFileTypeFromExtension(ext);
407 }
408 }
409
410 // check the fallbacks
411 // TODO linear search is potentially slow, perhaps we should use a sorted
412 // array?
413 size_t count = m_fallbacks.GetCount();
414 for ( size_t n = 0; n < count; n++ ) {
415 if ( wxMimeTypesManager::IsOfType(mimeType,
416 m_fallbacks[n].GetMimeType()) ) {
417 wxFileType *fileType = new wxFileType;
418 fileType->m_impl->Init(m_fallbacks[n]);
419
420 return fileType;
421 }
422 }
423
424 // unknown MIME type
425 return NULL;
426 }
427
428 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
429 {
430 // enumerate all keys under MIME_DATABASE_KEY
431 wxRegKey key(wxRegKey::HKCR, MIME_DATABASE_KEY);
432
433 wxString type;
434 long cookie;
435 bool cont = key.GetFirstKey(type, cookie);
436 while ( cont )
437 {
438 mimetypes.Add(type);
439
440 cont = key.GetNextKey(type, cookie);
441 }
442
443 return mimetypes.GetCount();
444 }
445
446
447 #endif
448 // wxUSE_FILE && wxUSE_TEXTFILE
449
450 #endif
451 // __WIN16__