wxMimeTypesManager now supports creating associations as well as querying
[wxWidgets.git] / src / common / mimecmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/mimecmn.cpp
3 // Purpose: classes and functions to manage MIME types
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Chris Elliott (biol75@york.ac.uk) 5 Dec 00: write support for Win32
7 // Created: 23.09.98
8 // RCS-ID: $Id$
9 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence: wxWindows license (part of wxExtra library)
11 /////////////////////////////////////////////////////////////////////////////
12
13 #ifdef __GNUG__
14 #pragma implementation "mimetypebase.h"
15 #endif
16
17 // for compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
19 #include "wx/module.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #ifndef WX_PRECOMP
26 #include "wx/defs.h"
27 #endif
28
29 #if (wxUSE_FILE && wxUSE_TEXTFILE) || defined(__WXMSW__)
30
31 #ifndef WX_PRECOMP
32 #include "wx/string.h"
33 #if wxUSE_GUI
34 #include "wx/icon.h"
35 #endif
36 #endif //WX_PRECOMP
37
38 // Doesn't compile in WIN16 mode
39 #ifndef __WIN16__
40
41 #include "wx/log.h"
42 #include "wx/file.h"
43 #include "wx/intl.h"
44 #include "wx/dynarray.h"
45 #include "wx/confbase.h"
46
47 #ifdef __WXMSW__
48 #include "wx/msw/registry.h"
49 #include "windows.h"
50 #elif defined(__UNIX__) || defined(__WXPM__)
51 #include "wx/ffile.h"
52 #include "wx/textfile.h"
53 #include "wx/dir.h"
54 #include "wx/utils.h"
55 #include "wx/tokenzr.h"
56 #endif // OS
57
58 #include "wx/mimetype.h"
59
60 // other standard headers
61 #include <ctype.h>
62
63 // in case we're compiling in non-GUI mode
64 class WXDLLEXPORT wxIcon;
65
66
67 // implementation classes:
68
69 #if defined(__WXMSW__)
70 #include "wx/msw/mimetype.h"
71 #elif defined (__WXMAC__)
72 #include "wx/mac/mimetype.h"
73 #elif defined (__WXPM__)
74 #include "wx/os2/mimetype.h"
75 #else // Unix
76 #include "wx/unix/mimetype.h"
77 #endif
78
79 // ============================================================================
80 // common classes
81 // ============================================================================
82
83 // ----------------------------------------------------------------------------
84 // wxFileTypeInfo
85 // ----------------------------------------------------------------------------
86
87 wxFileTypeInfo::wxFileTypeInfo(const char *mimeType,
88 const char *openCmd,
89 const char *printCmd,
90 const char *desc,
91 ...)
92 : m_mimeType(mimeType),
93 m_openCmd(openCmd),
94 m_printCmd(printCmd),
95 m_desc(desc)
96 {
97 va_list argptr;
98 va_start(argptr, desc);
99
100 for ( ;; )
101 {
102 const char *ext = va_arg(argptr, const char *);
103 if ( !ext )
104 {
105 // NULL terminates the list
106 break;
107 }
108
109 m_exts.Add(ext);
110 }
111
112 va_end(argptr);
113 }
114
115 #include "wx/arrimpl.cpp"
116 WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo);
117
118
119 // ============================================================================
120 // implementation of the wrapper classes
121 // ============================================================================
122
123 // ----------------------------------------------------------------------------
124 // wxFileType
125 // ----------------------------------------------------------------------------
126
127 wxString wxFileType::ExpandCommand(const wxString& command,
128 const wxFileType::MessageParameters& params)
129 {
130 bool hasFilename = FALSE;
131
132 wxString str;
133 for ( const wxChar *pc = command.c_str(); *pc != wxT('\0'); pc++ ) {
134 if ( *pc == wxT('%') ) {
135 switch ( *++pc ) {
136 case wxT('s'):
137 // '%s' expands into file name (quoted because it might
138 // contain spaces) - except if there are already quotes
139 // there because otherwise some programs may get confused
140 // by double double quotes
141 #if 0
142 if ( *(pc - 2) == wxT('"') )
143 str << params.GetFileName();
144 else
145 str << wxT('"') << params.GetFileName() << wxT('"');
146 #endif
147 str << params.GetFileName();
148 hasFilename = TRUE;
149 break;
150
151 case wxT('t'):
152 // '%t' expands into MIME type (quote it too just to be
153 // consistent)
154 str << wxT('\'') << params.GetMimeType() << wxT('\'');
155 break;
156
157 case wxT('{'):
158 {
159 const wxChar *pEnd = wxStrchr(pc, wxT('}'));
160 if ( pEnd == NULL ) {
161 wxString mimetype;
162 wxLogWarning(_("Unmatched '{' in an entry for mime type %s."),
163 params.GetMimeType().c_str());
164 str << wxT("%{");
165 }
166 else {
167 wxString param(pc + 1, pEnd - pc - 1);
168 str << wxT('\'') << params.GetParamValue(param) << wxT('\'');
169 pc = pEnd;
170 }
171 }
172 break;
173
174 case wxT('n'):
175 case wxT('F'):
176 // TODO %n is the number of parts, %F is an array containing
177 // the names of temp files these parts were written to
178 // and their mime types.
179 break;
180
181 default:
182 wxLogDebug(wxT("Unknown field %%%c in command '%s'."),
183 *pc, command.c_str());
184 str << *pc;
185 }
186 }
187 else {
188 str << *pc;
189 }
190 }
191
192 // metamail(1) man page states that if the mailcap entry doesn't have '%s'
193 // the program will accept the data on stdin so normally we should append
194 // "< %s" to the end of the command in such case, but not all commands
195 // behave like this, in particular a common test is 'test -n "$DISPLAY"'
196 // and appending "< %s" to this command makes the test fail... I don't
197 // know of the correct solution, try to guess what we have to do.
198 if ( !hasFilename && !str.IsEmpty()
199 #ifdef __UNIX__
200 && !str.StartsWith(_T("test "))
201 #endif // Unix
202 ) {
203 str << wxT(" < '") << params.GetFileName() << wxT('\'');
204 }
205
206 return str;
207 }
208
209 wxFileType::wxFileType()
210 {
211 m_impl = new wxFileTypeImpl;
212 }
213
214 wxFileType::~wxFileType()
215 {
216 delete m_impl;
217 }
218
219 bool wxFileType::GetExtensions(wxArrayString& extensions)
220 {
221 return m_impl->GetExtensions(extensions);
222 }
223
224 bool wxFileType::GetMimeType(wxString *mimeType) const
225 {
226 return m_impl->GetMimeType(mimeType);
227 }
228
229 bool wxFileType::GetMimeTypes(wxArrayString& mimeTypes) const
230 {
231 return m_impl->GetMimeTypes(mimeTypes);
232 }
233
234 bool wxFileType::GetIcon(wxIcon *icon,
235 wxString *iconFile,
236 int *iconIndex) const
237 {
238 #ifdef __WXMSW__
239 return m_impl->GetIcon(icon, iconFile, iconIndex);
240 #else
241 return m_impl->GetIcon(icon);
242 #endif
243 }
244
245 bool wxFileType::GetDescription(wxString *desc) const
246 {
247 return m_impl->GetDescription(desc);
248 }
249
250 bool
251 wxFileType::GetOpenCommand(wxString *openCmd,
252 const wxFileType::MessageParameters& params) const
253 {
254 return m_impl->GetOpenCommand(openCmd, params);
255 }
256
257 bool
258 wxFileType::GetPrintCommand(wxString *printCmd,
259 const wxFileType::MessageParameters& params) const
260 {
261 return m_impl->GetPrintCommand(printCmd, params);
262 }
263
264
265 size_t wxFileType::GetAllCommands(wxArrayString *verbs,
266 wxArrayString *commands,
267 const wxFileType::MessageParameters& params) const
268 {
269 if ( verbs )
270 verbs->Clear();
271 if ( commands )
272 commands->Clear();
273
274 #ifdef __WXMSW__
275 return m_impl->GetAllCommands(verbs, commands, params);
276 #else // !__WXMSW__
277 // we don't know how to retrieve all commands, so just try the 2 we know
278 // about
279 size_t count = 0;
280 wxString cmd;
281 if ( m_impl->GetOpenCommand(&cmd, params) )
282 {
283 if ( verbs )
284 verbs->Add(_T("Open"));
285 if ( commands )
286 commands->Add(cmd);
287 count++;
288 }
289
290 if ( GetPrintCommand(&cmd, params) )
291 {
292 if ( verbs )
293 verbs->Add(_T("Print"));
294 if ( commands )
295 commands->Add(cmd);
296
297 count++;
298 }
299
300 return count;
301 #endif // __WXMSW__/!__WXMSW__
302 }
303
304 bool wxFileType::SetOpenCommand(const wxString& cmd, bool overwriteprompt)
305 {
306 return SetCommand(cmd, _T("open"), overwriteprompt);
307 }
308
309 bool wxFileType::SetCommand(const wxString& cmd, const wxString& verb,
310 bool overwriteprompt)
311 {
312 #ifdef __WXMSW__
313 return m_impl->SetCommand(cmd, verb, overwriteprompt);
314 #else
315 wxFAIL_MSG(_T("not implemented"));
316
317 return FALSE;
318 #endif
319 }
320
321 bool wxFileType::SetMimeType(const wxString& mimeType)
322 {
323 // empty MIME type is meaningless here
324 wxCHECK_MSG( !mimeType.empty(), FALSE, _T("use RemoveMimeType()") );
325
326 #ifdef __WXMSW__
327 return m_impl->SetMimeType(mimeType);
328 #else
329 wxFAIL_MSG(_T("not implemented"));
330
331 return FALSE;
332 #endif
333 }
334
335 bool wxFileType::SetDefaultIcon(const wxString& cmd, int index)
336 {
337 wxString sTmp = cmd;
338 // VZ: should we do this?
339 if ( sTmp.empty() )
340 GetOpenCommand(&sTmp, wxFileType::MessageParameters("", ""));
341
342 wxCHECK_MSG( !sTmp.empty(), false, _T("need the icon file") );
343
344
345 #ifdef __WXMSW__
346 return m_impl->SetDefaultIcon (cmd, index);
347 #else
348 wxFAIL_MSG(_T("not implemented"));
349
350 return FALSE;
351 #endif
352 }
353
354 // now do remove functions
355 bool wxFileType::RemoveOpenCommand()
356 {
357 return RemoveCommand(_T("open"));
358 }
359
360 bool wxFileType::RemoveCommand(const wxString& verb)
361 {
362 #ifdef __WXMSW__
363 return m_impl->RemoveCommand(verb);
364 #else
365 wxFAIL_MSG(_T("not implemented"));
366
367 return FALSE;
368 #endif
369 }
370
371 bool wxFileType::RemoveMimeType()
372 {
373 #ifdef __WXMSW__
374 return m_impl->RemoveMimeType ();
375 #else
376 wxFAIL_MSG(_T("not implemented"));
377
378 return FALSE;
379 #endif
380 }
381
382 bool wxFileType::RemoveDefaultIcon()
383 {
384 #ifdef __WXMSW__
385 return m_impl->RemoveDefaultIcon();
386 #else
387 wxFAIL_MSG(_T("not implemented"));
388
389 return FALSE;
390 #endif
391 }
392
393 bool wxFileType::Unassociate()
394 {
395 bool result = TRUE;
396 if ( !RemoveOpenCommand() )
397 result = FALSE;
398 if ( !RemoveDefaultIcon() )
399 result = FALSE;
400 if ( !RemoveMimeType() )
401 result = FALSE;
402
403 // in MSW this leaves a HKCR.xzy key
404 return result;
405 }
406
407 // ----------------------------------------------------------------------------
408 // wxMimeTypesManager
409 // ----------------------------------------------------------------------------
410
411 void wxMimeTypesManager::EnsureImpl()
412 {
413 if ( !m_impl )
414 m_impl = new wxMimeTypesManagerImpl;
415 }
416
417 bool wxMimeTypesManager::IsOfType(const wxString& mimeType,
418 const wxString& wildcard)
419 {
420 wxASSERT_MSG( mimeType.Find(wxT('*')) == wxNOT_FOUND,
421 wxT("first MIME type can't contain wildcards") );
422
423 // all comparaisons are case insensitive (2nd arg of IsSameAs() is FALSE)
424 if ( wildcard.BeforeFirst(wxT('/')).IsSameAs(mimeType.BeforeFirst(wxT('/')), FALSE) )
425 {
426 wxString strSubtype = wildcard.AfterFirst(wxT('/'));
427
428 if ( strSubtype == wxT("*") ||
429 strSubtype.IsSameAs(mimeType.AfterFirst(wxT('/')), FALSE) )
430 {
431 // matches (either exactly or it's a wildcard)
432 return TRUE;
433 }
434 }
435
436 return FALSE;
437 }
438
439 wxMimeTypesManager::wxMimeTypesManager()
440 {
441 m_impl = NULL;
442 }
443
444 wxMimeTypesManager::~wxMimeTypesManager()
445 {
446 delete m_impl;
447 }
448
449 wxFileType *
450 wxMimeTypesManager::GetFileTypeFromExtension(const wxString& ext)
451 {
452 EnsureImpl();
453 return m_impl->GetFileTypeFromExtension(ext);
454 }
455
456 wxFileType *
457 wxMimeTypesManager::GetOrAllocateFileTypeFromExtension(const wxString& ext)
458 {
459 EnsureImpl();
460
461 #ifdef __WXMSW__
462 // this writes a root entry to the registry in HKCR.ext
463 return m_impl->GetOrAllocateFileTypeFromExtension(ext);
464 #else // !__WXMSW__
465 // VZ: "static const"??? (FIXME)
466 // just make a dummy entry with no writing to file
467 static const wxFileTypeInfo fallback[] =
468 {
469 wxFileTypeInfo("application/x-" + ext,
470 "",
471 "",
472 ext + " format file",
473 ext, NULL),
474 // must terminate the table with this!
475 wxFileTypeInfo()
476 };
477
478 AddFallbacks (fallback);
479 return m_impl->GetFileTypeFromExtension(ext);
480 #endif // __WXMSW__/!__WXMSW__
481 }
482
483
484 wxFileType *
485 wxMimeTypesManager::GetFileTypeFromMimeType(const wxString& mimeType)
486 {
487 EnsureImpl();
488 return m_impl->GetFileTypeFromMimeType(mimeType);
489 }
490
491 bool wxMimeTypesManager::ReadMailcap(const wxString& filename, bool fallback)
492 {
493 EnsureImpl();
494 return m_impl->ReadMailcap(filename, fallback);
495 }
496
497 bool wxMimeTypesManager::ReadMimeTypes(const wxString& filename)
498 {
499 EnsureImpl();
500 return m_impl->ReadMimeTypes(filename);
501 }
502
503 void wxMimeTypesManager::AddFallbacks(const wxFileTypeInfo *filetypes)
504 {
505 EnsureImpl();
506 for ( const wxFileTypeInfo *ft = filetypes; ft->IsValid(); ft++ ) {
507 m_impl->AddFallback(*ft);
508 }
509 }
510
511 size_t wxMimeTypesManager::EnumAllFileTypes(wxArrayString& mimetypes)
512 {
513 EnsureImpl();
514 return m_impl->EnumAllFileTypes(mimetypes);
515 }
516
517
518 // ----------------------------------------------------------------------------
519 // global data
520 // ----------------------------------------------------------------------------
521
522 // private object
523 static wxMimeTypesManager gs_mimeTypesManager;
524
525 // and public pointer
526 wxMimeTypesManager * wxTheMimeTypesManager = &gs_mimeTypesManager;
527
528 class wxMimeTypeCmnModule: public wxModule
529 {
530 public:
531 wxMimeTypeCmnModule() : wxModule() { }
532 virtual bool OnInit() { return TRUE; }
533 virtual void OnExit()
534 {
535 // this avoids false memory leak allerts:
536 if ( gs_mimeTypesManager.m_impl != NULL )
537 {
538 delete gs_mimeTypesManager.m_impl;
539 gs_mimeTypesManager.m_impl = NULL;
540 }
541 }
542
543 DECLARE_DYNAMIC_CLASS(wxMimeTypeCmnModule)
544 };
545
546 IMPLEMENT_DYNAMIC_CLASS(wxMimeTypeCmnModule, wxModule)
547
548 #endif
549 // wxUSE_FILE && wxUSE_TEXTFILE
550
551 #endif
552 // __WIN16__