use UTF8-encoded char* strings in UTF8 build instead of wchar_t* if the current local...
[wxWidgets.git] / src / common / mimecmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/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 licence (part of wxExtra library)
11 /////////////////////////////////////////////////////////////////////////////
12
13 // ============================================================================
14 // declarations
15 // ============================================================================
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 // for compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #if wxUSE_MIMETYPE
29
30 #include "wx/mimetype.h"
31
32 #ifndef WX_PRECOMP
33 #include "wx/dynarray.h"
34 #include "wx/string.h"
35 #include "wx/intl.h"
36 #include "wx/log.h"
37 #include "wx/module.h"
38 #endif //WX_PRECOMP
39
40 #include "wx/file.h"
41 #include "wx/iconloc.h"
42 #include "wx/confbase.h"
43
44 // other standard headers
45 #include <ctype.h>
46
47 // implementation classes:
48 #if defined(__WXMSW__)
49 #include "wx/msw/mimetype.h"
50 #elif defined(__WXMAC__)
51 #include "wx/mac/mimetype.h"
52 #elif defined(__WXPM__) || defined (__EMX__)
53 #include "wx/os2/mimetype.h"
54 #undef __UNIX__
55 #elif defined(__DOS__)
56 #include "wx/msdos/mimetype.h"
57 #else // Unix
58 #include "wx/unix/mimetype.h"
59 #endif
60
61 // ============================================================================
62 // common classes
63 // ============================================================================
64
65 // ----------------------------------------------------------------------------
66 // wxMimeTypeCommands
67 // ----------------------------------------------------------------------------
68
69 void
70 wxMimeTypeCommands::AddOrReplaceVerb(const wxString& verb, const wxString& cmd)
71 {
72 int n = m_verbs.Index(verb, false /* ignore case */);
73 if ( n == wxNOT_FOUND )
74 {
75 m_verbs.Add(verb);
76 m_commands.Add(cmd);
77 }
78 else
79 {
80 m_commands[n] = cmd;
81 }
82 }
83
84 wxString
85 wxMimeTypeCommands::GetCommandForVerb(const wxString& verb, size_t *idx) const
86 {
87 wxString s;
88
89 int n = m_verbs.Index(verb);
90 if ( n != wxNOT_FOUND )
91 {
92 s = m_commands[(size_t)n];
93 if ( idx )
94 *idx = n;
95 }
96 else if ( idx )
97 {
98 // different from any valid index
99 *idx = (size_t)-1;
100 }
101
102 return s;
103 }
104
105 wxString wxMimeTypeCommands::GetVerbCmd(size_t n) const
106 {
107 return m_verbs[n] + wxT('=') + m_commands[n];
108 }
109
110 // ----------------------------------------------------------------------------
111 // wxFileTypeInfo
112 // ----------------------------------------------------------------------------
113
114 void wxFileTypeInfo::VarArgInit(const wxString& mimeType,
115 const wxString& openCmd,
116 const wxString& printCmd,
117 const wxString& desc,
118 ...)
119 {
120 va_list argptr;
121 va_start(argptr, desc);
122
123 m_mimeType = mimeType;
124 m_openCmd = openCmd;
125 m_printCmd = printCmd;
126 m_desc = desc;
127
128 for ( ;; )
129 {
130 // icc gives this warning in its own va_arg() macro, argh
131 #ifdef __INTELC__
132 #pragma warning(push)
133 #pragma warning(disable: 1684)
134 #endif
135
136 wxArgNormalizedString ext(WX_VA_ARG_STRING(argptr));
137
138 #ifdef __INTELC__
139 #pragma warning(pop)
140 #endif
141 if ( !ext )
142 {
143 // NULL terminates the list
144 break;
145 }
146
147 m_exts.Add(ext.GetString());
148 }
149
150 va_end(argptr);
151 }
152
153
154 wxFileTypeInfo::wxFileTypeInfo(const wxArrayString& sArray)
155 {
156 m_mimeType = sArray [0u];
157 m_openCmd = sArray [1u];
158 m_printCmd = sArray [2u];
159 m_desc = sArray [3u];
160
161 size_t count = sArray.GetCount();
162 for ( size_t i = 4; i < count; i++ )
163 {
164 m_exts.Add(sArray[i]);
165 }
166 }
167
168 #include "wx/arrimpl.cpp"
169 WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo)
170
171 // ============================================================================
172 // implementation of the wrapper classes
173 // ============================================================================
174
175 // ----------------------------------------------------------------------------
176 // wxFileType
177 // ----------------------------------------------------------------------------
178
179 /* static */
180 wxString wxFileType::ExpandCommand(const wxString& command,
181 const wxFileType::MessageParameters& params)
182 {
183 bool hasFilename = false;
184
185 wxString str;
186 for ( const wxChar *pc = command.c_str(); *pc != wxT('\0'); pc++ ) {
187 if ( *pc == wxT('%') ) {
188 switch ( *++pc ) {
189 case wxT('s'):
190 // '%s' expands into file name (quoted because it might
191 // contain spaces) - except if there are already quotes
192 // there because otherwise some programs may get confused
193 // by double double quotes
194 #if 0
195 if ( *(pc - 2) == wxT('"') )
196 str << params.GetFileName();
197 else
198 str << wxT('"') << params.GetFileName() << wxT('"');
199 #endif
200 str << params.GetFileName();
201 hasFilename = true;
202 break;
203
204 case wxT('t'):
205 // '%t' expands into MIME type (quote it too just to be
206 // consistent)
207 str << wxT('\'') << params.GetMimeType() << wxT('\'');
208 break;
209
210 case wxT('{'):
211 {
212 const wxChar *pEnd = wxStrchr(pc, wxT('}'));
213 if ( pEnd == NULL ) {
214 wxString mimetype;
215 wxLogWarning(_("Unmatched '{' in an entry for mime type %s."),
216 params.GetMimeType().c_str());
217 str << wxT("%{");
218 }
219 else {
220 wxString param(pc + 1, pEnd - pc - 1);
221 str << wxT('\'') << params.GetParamValue(param) << wxT('\'');
222 pc = pEnd;
223 }
224 }
225 break;
226
227 case wxT('n'):
228 case wxT('F'):
229 // TODO %n is the number of parts, %F is an array containing
230 // the names of temp files these parts were written to
231 // and their mime types.
232 break;
233
234 default:
235 wxLogDebug(wxT("Unknown field %%%c in command '%s'."),
236 *pc, command.c_str());
237 str << *pc;
238 }
239 }
240 else {
241 str << *pc;
242 }
243 }
244
245 // metamail(1) man page states that if the mailcap entry doesn't have '%s'
246 // the program will accept the data on stdin so normally we should append
247 // "< %s" to the end of the command in such case, but not all commands
248 // behave like this, in particular a common test is 'test -n "$DISPLAY"'
249 // and appending "< %s" to this command makes the test fail... I don't
250 // know of the correct solution, try to guess what we have to do.
251
252 // test now carried out on reading file so test should never get here
253 if ( !hasFilename && !str.empty()
254 #ifdef __UNIX__
255 && !str.StartsWith(_T("test "))
256 #endif // Unix
257 ) {
258 str << wxT(" < '") << params.GetFileName() << wxT('\'');
259 }
260
261 return str;
262 }
263
264 wxFileType::wxFileType(const wxFileTypeInfo& info)
265 {
266 m_info = &info;
267 m_impl = NULL;
268 }
269
270 wxFileType::wxFileType()
271 {
272 m_info = NULL;
273 m_impl = new wxFileTypeImpl;
274 }
275
276 wxFileType::~wxFileType()
277 {
278 if ( m_impl )
279 delete m_impl;
280 }
281
282 bool wxFileType::GetExtensions(wxArrayString& extensions)
283 {
284 if ( m_info )
285 {
286 extensions = m_info->GetExtensions();
287 return true;
288 }
289
290 return m_impl->GetExtensions(extensions);
291 }
292
293 bool wxFileType::GetMimeType(wxString *mimeType) const
294 {
295 wxCHECK_MSG( mimeType, false, _T("invalid parameter in GetMimeType") );
296
297 if ( m_info )
298 {
299 *mimeType = m_info->GetMimeType();
300
301 return true;
302 }
303
304 return m_impl->GetMimeType(mimeType);
305 }
306
307 bool wxFileType::GetMimeTypes(wxArrayString& mimeTypes) const
308 {
309 if ( m_info )
310 {
311 mimeTypes.Clear();
312 mimeTypes.Add(m_info->GetMimeType());
313
314 return true;
315 }
316
317 return m_impl->GetMimeTypes(mimeTypes);
318 }
319
320 bool wxFileType::GetIcon(wxIconLocation *iconLoc) const
321 {
322 if ( m_info )
323 {
324 if ( iconLoc )
325 {
326 iconLoc->SetFileName(m_info->GetIconFile());
327 #ifdef __WXMSW__
328 iconLoc->SetIndex(m_info->GetIconIndex());
329 #endif // __WXMSW__
330 }
331
332 return true;
333 }
334
335 return m_impl->GetIcon(iconLoc);
336 }
337
338 bool
339 wxFileType::GetIcon(wxIconLocation *iconloc,
340 const MessageParameters& params) const
341 {
342 if ( !GetIcon(iconloc) )
343 {
344 return false;
345 }
346
347 // we may have "%s" in the icon location string, at least under Windows, so
348 // expand this
349 if ( iconloc )
350 {
351 iconloc->SetFileName(ExpandCommand(iconloc->GetFileName(), params));
352 }
353
354 return true;
355 }
356
357 bool wxFileType::GetDescription(wxString *desc) const
358 {
359 wxCHECK_MSG( desc, false, _T("invalid parameter in GetDescription") );
360
361 if ( m_info )
362 {
363 *desc = m_info->GetDescription();
364
365 return true;
366 }
367
368 return m_impl->GetDescription(desc);
369 }
370
371 bool
372 wxFileType::GetOpenCommand(wxString *openCmd,
373 const wxFileType::MessageParameters& params) const
374 {
375 wxCHECK_MSG( openCmd, false, _T("invalid parameter in GetOpenCommand") );
376
377 if ( m_info )
378 {
379 *openCmd = ExpandCommand(m_info->GetOpenCommand(), params);
380
381 return true;
382 }
383
384 return m_impl->GetOpenCommand(openCmd, params);
385 }
386
387 wxString wxFileType::GetOpenCommand(const wxString& filename) const
388 {
389 wxString cmd;
390 if ( !GetOpenCommand(&cmd, filename) )
391 {
392 // return empty string to indicate an error
393 cmd.clear();
394 }
395
396 return cmd;
397 }
398
399 bool
400 wxFileType::GetPrintCommand(wxString *printCmd,
401 const wxFileType::MessageParameters& params) const
402 {
403 wxCHECK_MSG( printCmd, false, _T("invalid parameter in GetPrintCommand") );
404
405 if ( m_info )
406 {
407 *printCmd = ExpandCommand(m_info->GetPrintCommand(), params);
408
409 return true;
410 }
411
412 return m_impl->GetPrintCommand(printCmd, params);
413 }
414
415
416 size_t wxFileType::GetAllCommands(wxArrayString *verbs,
417 wxArrayString *commands,
418 const wxFileType::MessageParameters& params) const
419 {
420 if ( verbs )
421 verbs->Clear();
422 if ( commands )
423 commands->Clear();
424
425 #if defined (__WXMSW__) || defined(__UNIX__)
426 return m_impl->GetAllCommands(verbs, commands, params);
427 #else // !__WXMSW__ || Unix
428 // we don't know how to retrieve all commands, so just try the 2 we know
429 // about
430 size_t count = 0;
431 wxString cmd;
432 if ( GetOpenCommand(&cmd, params) )
433 {
434 if ( verbs )
435 verbs->Add(_T("Open"));
436 if ( commands )
437 commands->Add(cmd);
438 count++;
439 }
440
441 if ( GetPrintCommand(&cmd, params) )
442 {
443 if ( verbs )
444 verbs->Add(_T("Print"));
445 if ( commands )
446 commands->Add(cmd);
447
448 count++;
449 }
450
451 return count;
452 #endif // __WXMSW__/| __UNIX__
453 }
454
455 bool wxFileType::Unassociate()
456 {
457 #if defined(__WXMSW__)
458 return m_impl->Unassociate();
459 #elif defined(__UNIX__)
460 return m_impl->Unassociate(this);
461 #else
462 wxFAIL_MSG( _T("not implemented") ); // TODO
463 return false;
464 #endif
465 }
466
467 bool wxFileType::SetCommand(const wxString& cmd,
468 const wxString& verb,
469 bool overwriteprompt)
470 {
471 #if defined (__WXMSW__) || defined(__UNIX__)
472 return m_impl->SetCommand(cmd, verb, overwriteprompt);
473 #else
474 wxUnusedVar(cmd);
475 wxUnusedVar(verb);
476 wxUnusedVar(overwriteprompt);
477 wxFAIL_MSG(_T("not implemented"));
478 return false;
479 #endif
480 }
481
482 bool wxFileType::SetDefaultIcon(const wxString& cmd, int index)
483 {
484 wxString sTmp = cmd;
485 #ifdef __WXMSW__
486 // VZ: should we do this?
487 // chris elliott : only makes sense in MS windows
488 if ( sTmp.empty() )
489 GetOpenCommand(&sTmp, wxFileType::MessageParameters(wxEmptyString, wxEmptyString));
490 #endif
491 wxCHECK_MSG( !sTmp.empty(), false, _T("need the icon file") );
492
493 #if defined (__WXMSW__) || defined(__UNIX__)
494 return m_impl->SetDefaultIcon (cmd, index);
495 #else
496 wxUnusedVar(index);
497 wxFAIL_MSG(_T("not implemented"));
498 return false;
499 #endif
500 }
501
502 // ----------------------------------------------------------------------------
503 // wxMimeTypesManagerFactory
504 // ----------------------------------------------------------------------------
505
506 wxMimeTypesManagerFactory *wxMimeTypesManagerFactory::m_factory = NULL;
507
508 /* static */
509 void wxMimeTypesManagerFactory::Set(wxMimeTypesManagerFactory *factory)
510 {
511 delete m_factory;
512
513 m_factory = factory;
514 }
515
516 /* static */
517 wxMimeTypesManagerFactory *wxMimeTypesManagerFactory::Get()
518 {
519 if ( !m_factory )
520 m_factory = new wxMimeTypesManagerFactory;
521
522 return m_factory;
523 }
524
525 wxMimeTypesManagerImpl *wxMimeTypesManagerFactory::CreateMimeTypesManagerImpl()
526 {
527 return new wxMimeTypesManagerImpl;
528 }
529
530 // ----------------------------------------------------------------------------
531 // wxMimeTypesManager
532 // ----------------------------------------------------------------------------
533
534 void wxMimeTypesManager::EnsureImpl()
535 {
536 if ( !m_impl )
537 m_impl = wxMimeTypesManagerFactory::Get()->CreateMimeTypesManagerImpl();
538 }
539
540 bool wxMimeTypesManager::IsOfType(const wxString& mimeType,
541 const wxString& wildcard)
542 {
543 wxASSERT_MSG( mimeType.Find(wxT('*')) == wxNOT_FOUND,
544 wxT("first MIME type can't contain wildcards") );
545
546 // all comparaisons are case insensitive (2nd arg of IsSameAs() is false)
547 if ( wildcard.BeforeFirst(wxT('/')).
548 IsSameAs(mimeType.BeforeFirst(wxT('/')), false) )
549 {
550 wxString strSubtype = wildcard.AfterFirst(wxT('/'));
551
552 if ( strSubtype == wxT("*") ||
553 strSubtype.IsSameAs(mimeType.AfterFirst(wxT('/')), false) )
554 {
555 // matches (either exactly or it's a wildcard)
556 return true;
557 }
558 }
559
560 return false;
561 }
562
563 wxMimeTypesManager::wxMimeTypesManager()
564 {
565 m_impl = NULL;
566 }
567
568 wxMimeTypesManager::~wxMimeTypesManager()
569 {
570 if ( m_impl )
571 delete m_impl;
572 }
573
574 bool wxMimeTypesManager::Unassociate(wxFileType *ft)
575 {
576 EnsureImpl();
577
578 #if defined(__UNIX__) && !defined(__CYGWIN__) && !defined(__WINE__)
579 return m_impl->Unassociate(ft);
580 #else
581 return ft->Unassociate();
582 #endif
583 }
584
585
586 wxFileType *
587 wxMimeTypesManager::Associate(const wxFileTypeInfo& ftInfo)
588 {
589 EnsureImpl();
590
591 #if defined(__WXMSW__) || defined(__UNIX__)
592 return m_impl->Associate(ftInfo);
593 #else // other platforms
594 wxUnusedVar(ftInfo);
595 wxFAIL_MSG( _T("not implemented") ); // TODO
596 return NULL;
597 #endif // platforms
598 }
599
600 wxFileType *
601 wxMimeTypesManager::GetFileTypeFromExtension(const wxString& ext)
602 {
603 EnsureImpl();
604 wxFileType *ft = m_impl->GetFileTypeFromExtension(ext);
605
606 if ( !ft ) {
607 // check the fallbacks
608 //
609 // TODO linear search is potentially slow, perhaps we should use a
610 // sorted array?
611 size_t count = m_fallbacks.GetCount();
612 for ( size_t n = 0; n < count; n++ ) {
613 if ( m_fallbacks[n].GetExtensions().Index(ext) != wxNOT_FOUND ) {
614 ft = new wxFileType(m_fallbacks[n]);
615
616 break;
617 }
618 }
619 }
620
621 return ft;
622 }
623
624 wxFileType *
625 wxMimeTypesManager::GetFileTypeFromMimeType(const wxString& mimeType)
626 {
627 EnsureImpl();
628 wxFileType *ft = m_impl->GetFileTypeFromMimeType(mimeType);
629
630 if ( !ft ) {
631 // check the fallbacks
632 //
633 // TODO linear search is potentially slow, perhaps we should use a
634 // sorted array?
635 size_t count = m_fallbacks.GetCount();
636 for ( size_t n = 0; n < count; n++ ) {
637 if ( wxMimeTypesManager::IsOfType(mimeType,
638 m_fallbacks[n].GetMimeType()) ) {
639 ft = new wxFileType(m_fallbacks[n]);
640
641 break;
642 }
643 }
644 }
645
646 return ft;
647 }
648
649 bool wxMimeTypesManager::ReadMailcap(const wxString& filename, bool fallback)
650 {
651 EnsureImpl();
652 return m_impl->ReadMailcap(filename, fallback);
653 }
654
655 bool wxMimeTypesManager::ReadMimeTypes(const wxString& filename)
656 {
657 EnsureImpl();
658 return m_impl->ReadMimeTypes(filename);
659 }
660
661 void wxMimeTypesManager::AddFallbacks(const wxFileTypeInfo *filetypes)
662 {
663 EnsureImpl();
664 for ( const wxFileTypeInfo *ft = filetypes; ft && ft->IsValid(); ft++ ) {
665 AddFallback(*ft);
666 }
667 }
668
669 size_t wxMimeTypesManager::EnumAllFileTypes(wxArrayString& mimetypes)
670 {
671 EnsureImpl();
672 size_t countAll = m_impl->EnumAllFileTypes(mimetypes);
673
674 // add the fallback filetypes
675 size_t count = m_fallbacks.GetCount();
676 for ( size_t n = 0; n < count; n++ ) {
677 if ( mimetypes.Index(m_fallbacks[n].GetMimeType()) == wxNOT_FOUND ) {
678 mimetypes.Add(m_fallbacks[n].GetMimeType());
679 countAll++;
680 }
681 }
682
683 return countAll;
684 }
685
686 void wxMimeTypesManager::Initialize(int mcapStyle,
687 const wxString& sExtraDir)
688 {
689 #if defined(__UNIX__) && !defined(__CYGWIN__) && !defined(__WINE__)
690 EnsureImpl();
691
692 m_impl->Initialize(mcapStyle, sExtraDir);
693 #else
694 (void)mcapStyle;
695 (void)sExtraDir;
696 #endif // Unix
697 }
698
699 // and this function clears all the data from the manager
700 void wxMimeTypesManager::ClearData()
701 {
702 #if defined(__UNIX__) && !defined(__CYGWIN__) && !defined(__WINE__)
703 EnsureImpl();
704
705 m_impl->ClearData();
706 #endif // Unix
707 }
708
709 // ----------------------------------------------------------------------------
710 // global data and wxMimeTypeCmnModule
711 // ----------------------------------------------------------------------------
712
713 // private object
714 static wxMimeTypesManager gs_mimeTypesManager;
715
716 // and public pointer
717 wxMimeTypesManager *wxTheMimeTypesManager = &gs_mimeTypesManager;
718
719 class wxMimeTypeCmnModule: public wxModule
720 {
721 public:
722 wxMimeTypeCmnModule() : wxModule() { }
723
724 virtual bool OnInit() { return true; }
725 virtual void OnExit()
726 {
727 wxMimeTypesManagerFactory::Set(NULL);
728
729 if ( gs_mimeTypesManager.m_impl != NULL )
730 {
731 delete gs_mimeTypesManager.m_impl;
732 gs_mimeTypesManager.m_impl = NULL;
733 gs_mimeTypesManager.m_fallbacks.Clear();
734 }
735 }
736
737 DECLARE_DYNAMIC_CLASS(wxMimeTypeCmnModule)
738 };
739
740 IMPLEMENT_DYNAMIC_CLASS(wxMimeTypeCmnModule, wxModule)
741
742 #endif // wxUSE_MIMETYPE