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