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