1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/mimetype.cpp
3 // Purpose: classes and functions to manage MIME types
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "mimetype.h"
16 // for compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
27 #if (wxUSE_FILE && wxUSE_TEXTFILE) || defined(__WXMSW__)
30 #include "wx/string.h"
34 // Doesn't compile in WIN16 mode
40 #include "wx/dynarray.h"
41 #include "wx/confbase.h"
44 #include "wx/msw/registry.h"
46 #elif defined(__UNIX__) || defined(__WXPM__)
48 #include "wx/textfile.h"
53 #include "wx/mimetype.h"
55 // other standard headers
58 // ----------------------------------------------------------------------------
60 // ----------------------------------------------------------------------------
62 // implementation classes, platform dependent
65 // These classes use Windows registry to retrieve the required information.
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.
73 // 2. "HKCR\.ext" contains
74 // a) unnamed value containing the "filetype"
75 // b) value "Content Type" containing the MIME type
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
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)
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\\");
93 wxFileTypeImpl() { m_info
= NULL
; }
95 // one of these Init() function must be called (ctor can't take any
96 // arguments because it's common)
98 // initialize us with our file type name and extension - in this case
99 // we will read all other data from the registry
100 void Init(const wxString
& strFileType
, const wxString
& ext
)
101 { m_strFileType
= strFileType
; m_ext
= ext
; }
103 // initialize us with a wxFileTypeInfo object - it contains all the
105 void Init(const wxFileTypeInfo
& info
)
108 // implement accessor functions
109 bool GetExtensions(wxArrayString
& extensions
);
110 bool GetMimeType(wxString
*mimeType
) const;
111 bool GetIcon(wxIcon
*icon
) const;
112 bool GetDescription(wxString
*desc
) const;
113 bool GetOpenCommand(wxString
*openCmd
,
114 const wxFileType::MessageParameters
& params
) const;
115 bool GetPrintCommand(wxString
*printCmd
,
116 const wxFileType::MessageParameters
& params
) const;
119 // helper function: reads the command corresponding to the specified verb
120 // from the registry (returns an empty string if not found)
121 wxString
GetCommand(const wxChar
*verb
) const;
123 // we use either m_info or read the data from the registry if m_info == NULL
124 const wxFileTypeInfo
*m_info
;
125 wxString m_strFileType
, // may be empty
129 WX_DECLARE_EXPORTED_OBJARRAY(wxFileTypeInfo
, wxArrayFileTypeInfo
);
130 #include "wx/arrimpl.cpp"
131 WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo
);
133 class wxMimeTypesManagerImpl
136 // nothing to do here, we don't load any data but just go and fetch it from
137 // the registry when asked for
138 wxMimeTypesManagerImpl() { }
140 // implement containing class functions
141 wxFileType
*GetFileTypeFromExtension(const wxString
& ext
);
142 wxFileType
*GetFileTypeFromMimeType(const wxString
& mimeType
);
144 size_t EnumAllFileTypes(wxArrayString
& mimetypes
);
146 // this are NOPs under Windows
147 bool ReadMailcap(const wxString
& filename
, bool fallback
= TRUE
)
149 bool ReadMimeTypes(const wxString
& filename
)
152 void AddFallback(const wxFileTypeInfo
& ft
) { m_fallbacks
.Add(ft
); }
155 wxArrayFileTypeInfo m_fallbacks
;
158 #elif defined( __WXMAC__ )
160 WX_DECLARE_EXPORTED_OBJARRAY(wxFileTypeInfo
, wxArrayFileTypeInfo
);
161 #include "wx/arrimpl.cpp"
162 WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo
);
164 class wxMimeTypesManagerImpl
167 wxMimeTypesManagerImpl() { }
169 // implement containing class functions
170 wxFileType
*GetFileTypeFromExtension(const wxString
& ext
);
171 wxFileType
*GetFileTypeFromMimeType(const wxString
& mimeType
);
173 size_t EnumAllFileTypes(wxArrayString
& mimetypes
);
175 // this are NOPs under MacOS
176 bool ReadMailcap(const wxString
& filename
, bool fallback
= TRUE
) { return TRUE
; }
177 bool ReadMimeTypes(const wxString
& filename
) { return TRUE
; }
179 void AddFallback(const wxFileTypeInfo
& ft
) { m_fallbacks
.Add(ft
); }
182 wxArrayFileTypeInfo m_fallbacks
;
188 // initialize us with our file type name
189 void SetFileType(const wxString
& strFileType
)
190 { m_strFileType
= strFileType
; }
191 void SetExt(const wxString
& ext
)
194 // implement accessor functions
195 bool GetExtensions(wxArrayString
& extensions
);
196 bool GetMimeType(wxString
*mimeType
) const;
197 bool GetIcon(wxIcon
*icon
) const;
198 bool GetDescription(wxString
*desc
) const;
199 bool GetOpenCommand(wxString
*openCmd
,
200 const wxFileType::MessageParameters
&) const
201 { return GetCommand(openCmd
, "open"); }
202 bool GetPrintCommand(wxString
*printCmd
,
203 const wxFileType::MessageParameters
&) const
204 { return GetCommand(printCmd
, "print"); }
208 bool GetCommand(wxString
*command
, const char *verb
) const;
210 wxString m_strFileType
, m_ext
;
215 // this class uses both mailcap and mime.types to gather information about file
218 // The information about mailcap file was extracted from metamail(1) sources and
221 // Format of mailcap file: spaces are ignored, each line is either a comment
222 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
223 // A backslash can be used to quote semicolons and newlines (and, in fact,
224 // anything else including itself).
226 // The first field is always the MIME type in the form of type/subtype (see RFC
227 // 822) where subtype may be '*' meaning "any". Following metamail, we accept
228 // "type" which means the same as "type/*", although I'm not sure whether this
231 // The second field is always the command to run. It is subject to
232 // parameter/filename expansion described below.
234 // All the following fields are optional and may not be present at all. If
235 // they're present they may appear in any order, although each of them should
236 // appear only once. The optional fields are the following:
237 // * notes=xxx is an uninterpreted string which is silently ignored
238 // * test=xxx is the command to be used to determine whether this mailcap line
239 // applies to our data or not. The RHS of this field goes through the
240 // parameter/filename expansion (as the 2nd field) and the resulting string
241 // is executed. The line applies only if the command succeeds, i.e. returns 0
243 // * print=xxx is the command to be used to print (and not view) the data of
244 // this type (parameter/filename expansion is done here too)
245 // * edit=xxx is the command to open/edit the data of this type
246 // * needsterminal means that a new console must be created for the viewer
247 // * copiousoutput means that the viewer doesn't interact with the user but
248 // produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
249 // good example), thus it might be a good idea to use some kind of paging
251 // * textualnewlines means not to perform CR/LF translation (not honored)
252 // * compose and composetyped fields are used to determine the program to be
253 // called to create a new message pert in the specified format (unused).
255 // Parameter/filename xpansion:
256 // * %s is replaced with the (full) file name
257 // * %t is replaced with MIME type/subtype of the entry
258 // * for multipart type only %n is replaced with the nnumber of parts and %F is
259 // replaced by an array of (content-type, temporary file name) pairs for all
260 // message parts (TODO)
261 // * %{parameter} is replaced with the value of parameter taken from
262 // Content-type header line of the message.
264 // FIXME any docs with real descriptions of these files??
266 // There are 2 possible formats for mime.types file, one entry per line (used
267 // for global mime.types) and "expanded" format where an entry takes multiple
268 // lines (used for users mime.types).
270 // For both formats spaces are ignored and lines starting with a '#' are
271 // comments. Each record has one of two following forms:
272 // a) for "brief" format:
273 // <mime type> <space separated list of extensions>
274 // b) for "expanded" format:
275 // type=<mime type> \ desc="<description>" \ exts="ext"
277 // We try to autodetect the format of mime.types: if a non-comment line starts
278 // with "type=" we assume the second format, otherwise the first one.
280 // there may be more than one entry for one and the same mime type, to
281 // choose the right one we have to run the command specified in the test
282 // field on our data.
287 MailCapEntry(const wxString
& openCmd
,
288 const wxString
& printCmd
,
289 const wxString
& testCmd
)
290 : m_openCmd(openCmd
), m_printCmd(printCmd
), m_testCmd(testCmd
)
296 const wxString
& GetOpenCmd() const { return m_openCmd
; }
297 const wxString
& GetPrintCmd() const { return m_printCmd
; }
298 const wxString
& GetTestCmd() const { return m_testCmd
; }
300 MailCapEntry
*GetNext() const { return m_next
; }
303 // prepend this element to the list
304 void Prepend(MailCapEntry
*next
) { m_next
= next
; }
305 // insert into the list at given position
306 void Insert(MailCapEntry
*next
, size_t pos
)
311 for ( cur
= next
; cur
!= NULL
; cur
= cur
->m_next
, n
++ ) {
316 wxASSERT_MSG( n
== pos
, wxT("invalid position in MailCapEntry::Insert") );
318 m_next
= cur
->m_next
;
321 // append this element to the list
322 void Append(MailCapEntry
*next
)
324 wxCHECK_RET( next
!= NULL
, wxT("Append()ing to what?") );
328 for ( cur
= next
; cur
->m_next
!= NULL
; cur
= cur
->m_next
)
333 wxASSERT_MSG( !m_next
, wxT("Append()ing element already in the list?") );
337 wxString m_openCmd
, // command to use to open/view the file
339 m_testCmd
; // only apply this entry if test yields
340 // true (i.e. the command returns 0)
342 MailCapEntry
*m_next
; // in the linked list
345 WX_DEFINE_ARRAY(MailCapEntry
*, ArrayTypeEntries
);
347 // the base class which may be used to find an icon for the MIME type
348 class wxMimeTypeIconHandler
351 virtual bool GetIcon(const wxString
& mimetype
, wxIcon
*icon
) = 0;
354 WX_DEFINE_ARRAY(wxMimeTypeIconHandler
*, ArrayIconHandlers
);
356 // the icon handler which uses GNOME MIME database
357 class wxGNOMEIconHandler
: public wxMimeTypeIconHandler
360 virtual bool GetIcon(const wxString
& mimetype
, wxIcon
*icon
);
364 void LoadIconsFromKeyFile(const wxString
& filename
);
365 void LoadKeyFilesFromDir(const wxString
& dirbase
);
367 static bool m_inited
;
369 static wxSortedArrayString ms_mimetypes
;
370 static wxArrayString ms_icons
;
373 // the icon handler which uses KDE MIME database
374 class wxKDEIconHandler
: public wxMimeTypeIconHandler
377 virtual bool GetIcon(const wxString
& mimetype
, wxIcon
*icon
);
380 void LoadLinksForMimeSubtype(const wxString
& dirbase
,
381 const wxString
& subdir
,
382 const wxString
& filename
);
383 void LoadLinksForMimeType(const wxString
& dirbase
,
384 const wxString
& subdir
);
385 void LoadLinkFilesFromDir(const wxString
& dirbase
);
388 static bool m_inited
;
390 static wxSortedArrayString ms_mimetypes
;
391 static wxArrayString ms_icons
;
394 // this is the real wxMimeTypesManager for Unix
395 class wxMimeTypesManagerImpl
397 friend class wxFileTypeImpl
; // give it access to m_aXXX variables
400 // ctor loads all info into memory for quicker access later on
401 // TODO it would be nice to load them all, but parse on demand only...
402 wxMimeTypesManagerImpl();
404 // implement containing class functions
405 wxFileType
*GetFileTypeFromExtension(const wxString
& ext
);
406 wxFileType
*GetFileTypeFromMimeType(const wxString
& mimeType
);
408 size_t EnumAllFileTypes(wxArrayString
& mimetypes
);
410 bool ReadMailcap(const wxString
& filename
, bool fallback
= FALSE
);
411 bool ReadMimeTypes(const wxString
& filename
);
413 void AddFallback(const wxFileTypeInfo
& filetype
);
415 // add information about the given mimetype
416 void AddMimeTypeInfo(const wxString
& mimetype
,
417 const wxString
& extensions
,
418 const wxString
& description
);
419 void AddMailcapInfo(const wxString
& strType
,
420 const wxString
& strOpenCmd
,
421 const wxString
& strPrintCmd
,
422 const wxString
& strTest
,
423 const wxString
& strDesc
);
426 // get the string containing space separated extensions for the given
428 wxString
GetExtension(size_t index
) { return m_aExtensions
[index
]; }
430 // get the array of icon handlers
431 static ArrayIconHandlers
& GetIconHandlers();
434 wxArrayString m_aTypes
, // MIME types
435 m_aDescriptions
, // descriptions (just some text)
436 m_aExtensions
; // space separated list of extensions
437 ArrayTypeEntries m_aEntries
; // commands and tests for this file type
439 // head of the linked list of the icon handlers
440 static ArrayIconHandlers ms_iconHandlers
;
446 // initialization functions
447 void Init(wxMimeTypesManagerImpl
*manager
, size_t index
)
448 { m_manager
= manager
; m_index
= index
; }
451 bool GetExtensions(wxArrayString
& extensions
);
452 bool GetMimeType(wxString
*mimeType
) const
453 { *mimeType
= m_manager
->m_aTypes
[m_index
]; return TRUE
; }
454 bool GetIcon(wxIcon
*icon
) const;
455 bool GetDescription(wxString
*desc
) const
456 { *desc
= m_manager
->m_aDescriptions
[m_index
]; return TRUE
; }
458 bool GetOpenCommand(wxString
*openCmd
,
459 const wxFileType::MessageParameters
& params
) const
461 return GetExpandedCommand(openCmd
, params
, TRUE
);
464 bool GetPrintCommand(wxString
*printCmd
,
465 const wxFileType::MessageParameters
& params
) const
467 return GetExpandedCommand(printCmd
, params
, FALSE
);
471 // get the entry which passes the test (may return NULL)
472 MailCapEntry
*GetEntry(const wxFileType::MessageParameters
& params
) const;
474 // choose the correct entry to use and expand the command
475 bool GetExpandedCommand(wxString
*expandedCmd
,
476 const wxFileType::MessageParameters
& params
,
479 wxMimeTypesManagerImpl
*m_manager
;
480 size_t m_index
; // in the wxMimeTypesManagerImpl arrays
485 // ============================================================================
487 // ============================================================================
489 // ----------------------------------------------------------------------------
491 // ----------------------------------------------------------------------------
493 wxFileTypeInfo::wxFileTypeInfo(const char *mimeType
,
495 const char *printCmd
,
498 : m_mimeType(mimeType
),
500 m_printCmd(printCmd
),
504 va_start(argptr
, desc
);
508 const char *ext
= va_arg(argptr
, const char *);
511 // NULL terminates the list
521 // ============================================================================
522 // implementation of the wrapper classes
523 // ============================================================================
525 // ----------------------------------------------------------------------------
527 // ----------------------------------------------------------------------------
529 wxString
wxFileType::ExpandCommand(const wxString
& command
,
530 const wxFileType::MessageParameters
& params
)
532 bool hasFilename
= FALSE
;
535 for ( const wxChar
*pc
= command
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
536 if ( *pc
== wxT('%') ) {
539 // '%s' expands into file name (quoted because it might
540 // contain spaces) - except if there are already quotes
541 // there because otherwise some programs may get confused
542 // by double double quotes
544 if ( *(pc
- 2) == wxT('"') )
545 str
<< params
.GetFileName();
547 str
<< wxT('"') << params
.GetFileName() << wxT('"');
549 str
<< params
.GetFileName();
554 // '%t' expands into MIME type (quote it too just to be
556 str
<< wxT('\'') << params
.GetMimeType() << wxT('\'');
561 const wxChar
*pEnd
= wxStrchr(pc
, wxT('}'));
562 if ( pEnd
== NULL
) {
564 wxLogWarning(_("Unmatched '{' in an entry for "
566 params
.GetMimeType().c_str());
570 wxString
param(pc
+ 1, pEnd
- pc
- 1);
571 str
<< wxT('\'') << params
.GetParamValue(param
) << wxT('\'');
579 // TODO %n is the number of parts, %F is an array containing
580 // the names of temp files these parts were written to
581 // and their mime types.
585 wxLogDebug(wxT("Unknown field %%%c in command '%s'."),
586 *pc
, command
.c_str());
595 // metamail(1) man page states that if the mailcap entry doesn't have '%s'
596 // the program will accept the data on stdin: so give it to it!
597 if ( !hasFilename
&& !str
.IsEmpty() ) {
598 str
<< wxT(" < '") << params
.GetFileName() << wxT('\'');
604 wxFileType::wxFileType()
606 m_impl
= new wxFileTypeImpl
;
609 wxFileType::~wxFileType()
614 bool wxFileType::GetExtensions(wxArrayString
& extensions
)
616 return m_impl
->GetExtensions(extensions
);
619 bool wxFileType::GetMimeType(wxString
*mimeType
) const
621 return m_impl
->GetMimeType(mimeType
);
624 bool wxFileType::GetIcon(wxIcon
*icon
) const
626 return m_impl
->GetIcon(icon
);
629 bool wxFileType::GetDescription(wxString
*desc
) const
631 return m_impl
->GetDescription(desc
);
635 wxFileType::GetOpenCommand(wxString
*openCmd
,
636 const wxFileType::MessageParameters
& params
) const
638 return m_impl
->GetOpenCommand(openCmd
, params
);
642 wxFileType::GetPrintCommand(wxString
*printCmd
,
643 const wxFileType::MessageParameters
& params
) const
645 return m_impl
->GetPrintCommand(printCmd
, params
);
648 // ----------------------------------------------------------------------------
649 // wxMimeTypesManager
650 // ----------------------------------------------------------------------------
652 bool wxMimeTypesManager::IsOfType(const wxString
& mimeType
,
653 const wxString
& wildcard
)
655 wxASSERT_MSG( mimeType
.Find(wxT('*')) == wxNOT_FOUND
,
656 wxT("first MIME type can't contain wildcards") );
658 // all comparaisons are case insensitive (2nd arg of IsSameAs() is FALSE)
659 if ( wildcard
.BeforeFirst(wxT('/')).IsSameAs(mimeType
.BeforeFirst(wxT('/')), FALSE
) )
661 wxString strSubtype
= wildcard
.AfterFirst(wxT('/'));
663 if ( strSubtype
== wxT("*") ||
664 strSubtype
.IsSameAs(mimeType
.AfterFirst(wxT('/')), FALSE
) )
666 // matches (either exactly or it's a wildcard)
674 wxMimeTypesManager::wxMimeTypesManager()
676 m_impl
= new wxMimeTypesManagerImpl
;
679 wxMimeTypesManager::~wxMimeTypesManager()
685 wxMimeTypesManager::GetFileTypeFromExtension(const wxString
& ext
)
687 return m_impl
->GetFileTypeFromExtension(ext
);
691 wxMimeTypesManager::GetFileTypeFromMimeType(const wxString
& mimeType
)
693 return m_impl
->GetFileTypeFromMimeType(mimeType
);
696 bool wxMimeTypesManager::ReadMailcap(const wxString
& filename
, bool fallback
)
698 return m_impl
->ReadMailcap(filename
, fallback
);
701 bool wxMimeTypesManager::ReadMimeTypes(const wxString
& filename
)
703 return m_impl
->ReadMimeTypes(filename
);
706 void wxMimeTypesManager::AddFallbacks(const wxFileTypeInfo
*filetypes
)
708 for ( const wxFileTypeInfo
*ft
= filetypes
; ft
->IsValid(); ft
++ ) {
709 m_impl
->AddFallback(*ft
);
713 size_t wxMimeTypesManager::EnumAllFileTypes(wxArrayString
& mimetypes
)
715 return m_impl
->EnumAllFileTypes(mimetypes
);
718 // ============================================================================
719 // real (OS specific) implementation
720 // ============================================================================
724 wxString
wxFileTypeImpl::GetCommand(const wxChar
*verb
) const
726 // suppress possible error messages
730 if ( wxRegKey(wxRegKey::HKCR
, m_ext
+ _T("\\shell")).Exists() )
732 if ( wxRegKey(wxRegKey::HKCR
, m_strFileType
+ _T("\\shell")).Exists() )
733 strKey
= m_strFileType
;
738 return wxEmptyString
;
741 strKey
<< wxT("\\shell\\") << verb
<< wxT("\\command");
742 wxRegKey
key(wxRegKey::HKCR
, strKey
);
745 // it's the default value of the key
746 if ( key
.QueryValue(wxT(""), command
) ) {
747 // transform it from '%1' to '%s' style format string
749 // NB: we don't make any attempt to verify that the string is valid,
750 // i.e. doesn't contain %2, or second %1 or .... But we do make
751 // sure that we return a string with _exactly_ one '%s'!
752 bool foundFilename
= FALSE
;
753 size_t len
= command
.Len();
754 for ( size_t n
= 0; (n
< len
) && !foundFilename
; n
++ ) {
755 if ( command
[n
] == wxT('%') &&
756 (n
+ 1 < len
) && command
[n
+ 1] == wxT('1') ) {
757 // replace it with '%s'
758 command
[n
+ 1] = wxT('s');
760 foundFilename
= TRUE
;
764 if ( !foundFilename
) {
765 // we didn't find any '%1'!
766 // HACK: append the filename at the end, hope that it will do
767 command
<< wxT(" %s");
771 //else: no such file type or no value, will return empty string
777 wxFileTypeImpl::GetOpenCommand(wxString
*openCmd
,
778 const wxFileType::MessageParameters
& params
)
783 cmd
= m_info
->GetOpenCommand();
786 cmd
= GetCommand(wxT("open"));
789 *openCmd
= wxFileType::ExpandCommand(cmd
, params
);
791 return !openCmd
->IsEmpty();
795 wxFileTypeImpl::GetPrintCommand(wxString
*printCmd
,
796 const wxFileType::MessageParameters
& params
)
801 cmd
= m_info
->GetPrintCommand();
804 cmd
= GetCommand(wxT("print"));
807 *printCmd
= wxFileType::ExpandCommand(cmd
, params
);
809 return !printCmd
->IsEmpty();
812 // TODO this function is half implemented
813 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
816 extensions
= m_info
->GetExtensions();
820 else if ( m_ext
.IsEmpty() ) {
821 // the only way to get the list of extensions from the file type is to
822 // scan through all extensions in the registry - too slow...
827 extensions
.Add(m_ext
);
829 // it's a lie too, we don't return _all_ extensions...
834 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
837 // we already have it
838 *mimeType
= m_info
->GetMimeType();
843 // suppress possible error messages
845 wxRegKey
key(wxRegKey::HKCR
, wxT(".") + m_ext
);
846 if ( key
.Open() && key
.QueryValue(wxT("Content Type"), *mimeType
) ) {
854 bool wxFileTypeImpl::GetIcon(wxIcon
*icon
) const
858 // we don't have icons in the fallback resources
863 strIconKey
<< m_strFileType
<< wxT("\\DefaultIcon");
865 // suppress possible error messages
867 wxRegKey
key(wxRegKey::HKCR
, strIconKey
);
871 // it's the default value of the key
872 if ( key
.QueryValue(wxT(""), strIcon
) ) {
873 // the format is the following: <full path to file>, <icon index>
874 // NB: icon index may be negative as well as positive and the full
875 // path may contain the environment variables inside '%'
876 wxString strFullPath
= strIcon
.BeforeLast(wxT(',')),
877 strIndex
= strIcon
.AfterLast(wxT(','));
879 // index may be omitted, in which case BeforeLast(',') is empty and
880 // AfterLast(',') is the whole string
881 if ( strFullPath
.IsEmpty() ) {
882 strFullPath
= strIndex
;
886 wxString strExpPath
= wxExpandEnvVars(strFullPath
);
887 int nIndex
= wxAtoi(strIndex
);
889 HICON hIcon
= ExtractIcon(GetModuleHandle(NULL
), strExpPath
, nIndex
);
890 switch ( (int)hIcon
) {
891 case 0: // means no icons were found
892 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
893 wxLogDebug(wxT("incorrect registry entry '%s': no such icon."),
894 key
.GetName().c_str());
898 icon
->SetHICON((WXHICON
)hIcon
);
904 // no such file type or no value or incorrect icon entry
910 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
913 // we already have it
914 *desc
= m_info
->GetDescription();
919 // suppress possible error messages
921 wxRegKey
key(wxRegKey::HKCR
, m_strFileType
);
924 // it's the default value of the key
925 if ( key
.QueryValue(wxT(""), *desc
) ) {
933 // extension -> file type
935 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
)
937 // add the leading point if necessary
939 if ( ext
[0u] != wxT('.') ) {
944 // suppress possible error messages
947 bool knownExtension
= FALSE
;
949 wxString strFileType
;
950 wxRegKey
key(wxRegKey::HKCR
, str
);
952 // it's the default value of the key
953 if ( key
.QueryValue(wxT(""), strFileType
) ) {
954 // create the new wxFileType object
955 wxFileType
*fileType
= new wxFileType
;
956 fileType
->m_impl
->Init(strFileType
, ext
);
961 // this extension doesn't have a filetype, but it's known to the
962 // system and may be has some other useful keys (open command or
963 // content-type), so still return a file type object for it
964 knownExtension
= TRUE
;
968 // check the fallbacks
969 // TODO linear search is potentially slow, perhaps we should use a sorted
971 size_t count
= m_fallbacks
.GetCount();
972 for ( size_t n
= 0; n
< count
; n
++ ) {
973 if ( m_fallbacks
[n
].GetExtensions().Index(ext
) != wxNOT_FOUND
) {
974 wxFileType
*fileType
= new wxFileType
;
975 fileType
->m_impl
->Init(m_fallbacks
[n
]);
981 if ( knownExtension
)
983 wxFileType
*fileType
= new wxFileType
;
984 fileType
->m_impl
->Init(wxEmptyString
, ext
);
995 // MIME type -> extension -> file type
997 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
999 wxString strKey
= MIME_DATABASE_KEY
;
1002 // suppress possible error messages
1006 wxRegKey
key(wxRegKey::HKCR
, strKey
);
1008 if ( key
.QueryValue(wxT("Extension"), ext
) ) {
1009 return GetFileTypeFromExtension(ext
);
1013 // check the fallbacks
1014 // TODO linear search is potentially slow, perhaps we should use a sorted
1016 size_t count
= m_fallbacks
.GetCount();
1017 for ( size_t n
= 0; n
< count
; n
++ ) {
1018 if ( wxMimeTypesManager::IsOfType(mimeType
,
1019 m_fallbacks
[n
].GetMimeType()) ) {
1020 wxFileType
*fileType
= new wxFileType
;
1021 fileType
->m_impl
->Init(m_fallbacks
[n
]);
1027 // unknown MIME type
1031 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
1033 // enumerate all keys under MIME_DATABASE_KEY
1034 wxRegKey
key(wxRegKey::HKCR
, MIME_DATABASE_KEY
);
1038 bool cont
= key
.GetFirstKey(type
, cookie
);
1041 mimetypes
.Add(type
);
1043 cont
= key
.GetNextKey(type
, cookie
);
1046 return mimetypes
.GetCount();
1049 #elif defined ( __WXMAC__ )
1051 bool wxFileTypeImpl::GetCommand(wxString
*command
, const char *verb
) const
1056 // @@ this function is half implemented
1057 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
1062 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
1064 if ( m_strFileType
.Length() > 0 )
1066 *mimeType
= m_strFileType
;
1073 bool wxFileTypeImpl::GetIcon(wxIcon
*icon
) const
1075 // no such file type or no value or incorrect icon entry
1079 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
1084 // extension -> file type
1086 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& e
)
1092 wxFileType
*fileType
= new wxFileType
;
1093 fileType
->m_impl
->SetFileType("text/text");
1094 fileType
->m_impl
->SetExt(ext
);
1097 else if ( ext
== "htm" || ext
== "html" )
1099 wxFileType
*fileType
= new wxFileType
;
1100 fileType
->m_impl
->SetFileType("text/html");
1101 fileType
->m_impl
->SetExt(ext
);
1104 else if ( ext
== "gif" )
1106 wxFileType
*fileType
= new wxFileType
;
1107 fileType
->m_impl
->SetFileType("image/gif");
1108 fileType
->m_impl
->SetExt(ext
);
1111 else if ( ext
== "png" )
1113 wxFileType
*fileType
= new wxFileType
;
1114 fileType
->m_impl
->SetFileType("image/png");
1115 fileType
->m_impl
->SetExt(ext
);
1118 else if ( ext
== "jpg" || ext
== "jpeg" )
1120 wxFileType
*fileType
= new wxFileType
;
1121 fileType
->m_impl
->SetFileType("image/jpeg");
1122 fileType
->m_impl
->SetExt(ext
);
1125 else if ( ext
== "bmp" )
1127 wxFileType
*fileType
= new wxFileType
;
1128 fileType
->m_impl
->SetFileType("image/bmp");
1129 fileType
->m_impl
->SetExt(ext
);
1132 else if ( ext
== "tif" || ext
== "tiff" )
1134 wxFileType
*fileType
= new wxFileType
;
1135 fileType
->m_impl
->SetFileType("image/tiff");
1136 fileType
->m_impl
->SetExt(ext
);
1139 else if ( ext
== "xpm" )
1141 wxFileType
*fileType
= new wxFileType
;
1142 fileType
->m_impl
->SetFileType("image/xpm");
1143 fileType
->m_impl
->SetExt(ext
);
1146 else if ( ext
== "xbm" )
1148 wxFileType
*fileType
= new wxFileType
;
1149 fileType
->m_impl
->SetFileType("image/xbm");
1150 fileType
->m_impl
->SetExt(ext
);
1154 // unknown extension
1158 // MIME type -> extension -> file type
1160 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
1165 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
1167 wxFAIL_MSG( _T("TODO") ); // VZ: don't know anything about this for Mac
1174 // ============================================================================
1175 // Unix implementation
1176 // ============================================================================
1178 // ----------------------------------------------------------------------------
1180 // ----------------------------------------------------------------------------
1182 static wxGNOMEIconHandler gs_iconHandlerGNOME
;
1183 static wxKDEIconHandler gs_iconHandlerKDE
;
1185 bool wxGNOMEIconHandler::m_inited
= FALSE
;
1186 wxSortedArrayString
wxGNOMEIconHandler::ms_mimetypes
;
1187 wxArrayString
wxGNOMEIconHandler::ms_icons
;
1189 bool wxKDEIconHandler::m_inited
= FALSE
;
1190 wxSortedArrayString
wxKDEIconHandler::ms_mimetypes
;
1191 wxArrayString
wxKDEIconHandler::ms_icons
;
1193 ArrayIconHandlers
wxMimeTypesManagerImpl::ms_iconHandlers
;
1195 // ----------------------------------------------------------------------------
1196 // wxGNOMEIconHandler
1197 // ----------------------------------------------------------------------------
1199 // GNOME stores the info we're interested in in several locations:
1200 // 1. xxx.keys files under /usr/share/mime-info
1201 // 2. xxx.keys files under ~/.gnome/mime-info
1203 // The format of xxx.keys file is the following:
1205 // mimetype/subtype:
1208 // with blank lines separating the entries and indented lines starting with
1209 // TABs. We're interested in the field icon-filename whose value is the path
1210 // containing the icon.
1212 void wxGNOMEIconHandler::LoadIconsFromKeyFile(const wxString
& filename
)
1214 wxTextFile
textfile(filename
);
1215 if ( !textfile
.Open() )
1218 // values for the entry being parsed
1219 wxString curMimeType
, curIconFile
;
1222 size_t nLineCount
= textfile
.GetLineCount();
1223 for ( size_t nLine
= 0; ; nLine
++ )
1225 if ( nLine
< nLineCount
)
1227 pc
= textfile
[nLine
].c_str();
1228 if ( *pc
== _T('#') )
1236 // so that we will fall into the "if" below
1243 if ( !!curMimeType
&& !!curIconFile
)
1245 // do we already know this mimetype?
1246 int i
= ms_mimetypes
.Index(curMimeType
);
1247 if ( i
== wxNOT_FOUND
)
1250 size_t n
= ms_mimetypes
.Add(curMimeType
);
1251 ms_icons
.Insert(curIconFile
, n
);
1255 // replace the existing one (this means that the directories
1256 // should be searched in order of increased priority!)
1257 ms_icons
[(size_t)i
] = curIconFile
;
1263 // the end - this can only happen if nLine == nLineCount
1267 curIconFile
.Empty();
1272 // what do we have here?
1273 if ( *pc
== _T('\t') )
1275 // this is a field=value ling
1276 pc
++; // skip leading TAB
1278 static const int lenField
= 13; // strlen("icon-filename")
1279 if ( wxStrncmp(pc
, _T("icon-filename"), lenField
) == 0 )
1281 // skip '=' which follows and take everything left until the end
1283 curIconFile
= pc
+ lenField
+ 1;
1285 //else: some other field, we don't care
1289 // this is the start of the new section
1290 curMimeType
.Empty();
1292 while ( *pc
!= _T(':') && *pc
!= _T('\0') )
1294 curMimeType
+= *pc
++;
1299 // we reached the end of line without finding the colon,
1300 // something is wrong - ignore this line completely
1301 wxLogDebug(_T("Unreckognized line %d in file '%s' ignored"),
1302 nLine
+ 1, filename
.c_str());
1310 void wxGNOMEIconHandler::LoadKeyFilesFromDir(const wxString
& dirbase
)
1312 wxASSERT_MSG( !!dirbase
&& !wxEndsWithPathSeparator(dirbase
),
1313 _T("base directory shouldn't end with a slash") );
1315 wxString dirname
= dirbase
;
1316 dirname
<< _T("/mime-info");
1318 if ( !wxDir::Exists(dirname
) )
1322 if ( !dir
.IsOpened() )
1325 // we will concatenate it with filename to get the full path below
1329 bool cont
= dir
.GetFirst(&filename
, _T("*.keys"), wxDIR_FILES
);
1332 LoadIconsFromKeyFile(dirname
+ filename
);
1334 cont
= dir
.GetNext(&filename
);
1338 void wxGNOMEIconHandler::Init()
1341 dirs
.Add(_T("/usr/share"));
1344 wxGetHomeDir( &gnomedir
);
1345 gnomedir
+= _T("/.gnome");
1346 dirs
.Add( gnomedir
);
1348 size_t nDirs
= dirs
.GetCount();
1349 for ( size_t nDir
= 0; nDir
< nDirs
; nDir
++ )
1351 LoadKeyFilesFromDir(dirs
[nDir
]);
1357 bool wxGNOMEIconHandler::GetIcon(const wxString
& mimetype
, wxIcon
*icon
)
1364 int index
= ms_mimetypes
.Index(mimetype
);
1365 if ( index
== wxNOT_FOUND
)
1368 wxString iconname
= ms_icons
[(size_t)index
];
1371 *icon
= wxIcon(iconname
);
1373 // helpful for testing in console mode
1374 wxLogDebug(_T("Found GNOME icon for '%s': '%s'\n"),
1375 mimetype
.c_str(), iconname
.c_str());
1381 // ----------------------------------------------------------------------------
1383 // ----------------------------------------------------------------------------
1385 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype
1386 // may be found in either of the following locations
1388 // 1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
1389 // 2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
1391 // The format of a .kdelnk file is almost the same as the one used by
1392 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the
1393 // value for the entry "Type"
1395 void wxKDEIconHandler::LoadLinksForMimeSubtype(const wxString
& dirbase
,
1396 const wxString
& subdir
,
1397 const wxString
& filename
)
1399 wxFFile
file(dirbase
+ filename
);
1400 if ( !file
.IsOpened() )
1403 // these files are small, slurp the entire file at once
1405 if ( !file
.ReadAll(&text
) )
1408 int pos
= text
.Find(_T("Icon="));
1409 if ( pos
== wxNOT_FOUND
)
1417 const wxChar
*pc
= text
.c_str() + pos
+ 5; // 5 == strlen("Icon=")
1418 while ( *pc
&& *pc
!= _T('\n') )
1425 // don't check that the file actually exists - would be too slow
1426 icon
.Prepend(_T("/usr/share/icons/"));
1428 // construct mimetype from the directory name and the basename of the
1429 // file (it always has .kdelnk extension)
1431 mimetype
<< subdir
<< _T('/') << filename
.BeforeLast(_T('.'));
1433 // do we already have this MIME type?
1434 int i
= ms_mimetypes
.Index(mimetype
);
1435 if ( i
== wxNOT_FOUND
)
1438 size_t n
= ms_mimetypes
.Add(mimetype
);
1439 ms_icons
.Insert(icon
, n
);
1443 // replace the old value
1444 ms_icons
[(size_t)i
] = icon
;
1449 void wxKDEIconHandler::LoadLinksForMimeType(const wxString
& dirbase
,
1450 const wxString
& subdir
)
1452 wxString dirname
= dirbase
;
1455 if ( !dir
.IsOpened() )
1461 bool cont
= dir
.GetFirst(&filename
, _T("*.kdelnk"), wxDIR_FILES
);
1464 LoadLinksForMimeSubtype(dirname
, subdir
, filename
);
1466 cont
= dir
.GetNext(&filename
);
1470 void wxKDEIconHandler::LoadLinkFilesFromDir(const wxString
& dirbase
)
1472 wxASSERT_MSG( !!dirbase
&& !wxEndsWithPathSeparator(dirbase
),
1473 _T("base directory shouldn't end with a slash") );
1475 wxString dirname
= dirbase
;
1476 dirname
<< _T("/mimelnk");
1478 if ( !wxDir::Exists(dirname
) )
1482 if ( !dir
.IsOpened() )
1485 // we will concatenate it with dir name to get the full path below
1489 bool cont
= dir
.GetFirst(&subdir
, wxEmptyString
, wxDIR_DIRS
);
1492 LoadLinksForMimeType(dirname
, subdir
);
1494 cont
= dir
.GetNext(&subdir
);
1498 void wxKDEIconHandler::Init()
1502 // the variable KDEDIR is set when KDE is running
1503 const char *kdedir
= getenv("KDEDIR");
1506 dirs
.Add(wxString(kdedir
) + _T("/share"));
1510 // try to guess KDEDIR
1511 dirs
.Add(_T("/usr/share"));
1512 dirs
.Add(_T("/opt/kde/share"));
1515 dirs
.Add(wxGetHomeDir() + _T("/.kde/share"));
1517 size_t nDirs
= dirs
.GetCount();
1518 for ( size_t nDir
= 0; nDir
< nDirs
; nDir
++ )
1520 LoadLinkFilesFromDir(dirs
[nDir
]);
1526 bool wxKDEIconHandler::GetIcon(const wxString
& mimetype
, wxIcon
*icon
)
1533 int index
= ms_mimetypes
.Index(mimetype
);
1534 if ( index
== wxNOT_FOUND
)
1537 wxString iconname
= ms_icons
[(size_t)index
];
1540 *icon
= wxIcon(iconname
);
1542 // helpful for testing in console mode
1543 wxLogDebug(_T("Found KDE icon for '%s': '%s'\n"),
1544 mimetype
.c_str(), iconname
.c_str());
1550 // ----------------------------------------------------------------------------
1551 // wxFileTypeImpl (Unix)
1552 // ----------------------------------------------------------------------------
1555 wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters
& params
) const
1558 MailCapEntry
*entry
= m_manager
->m_aEntries
[m_index
];
1559 while ( entry
!= NULL
) {
1560 // notice that an empty command would always succeed (it's ok)
1561 command
= wxFileType::ExpandCommand(entry
->GetTestCmd(), params
);
1563 if ( command
.IsEmpty() || (wxSystem(command
) == 0) ) {
1565 wxLogTrace(wxT("Test '%s' for mime type '%s' succeeded."),
1566 command
.c_str(), params
.GetMimeType().c_str());
1570 wxLogTrace(wxT("Test '%s' for mime type '%s' failed."),
1571 command
.c_str(), params
.GetMimeType().c_str());
1574 entry
= entry
->GetNext();
1580 bool wxFileTypeImpl::GetIcon(wxIcon
*icon
) const
1583 (void)GetMimeType(&mimetype
);
1585 ArrayIconHandlers
& handlers
= m_manager
->GetIconHandlers();
1586 size_t count
= handlers
.GetCount();
1587 for ( size_t n
= 0; n
< count
; n
++ )
1589 if ( handlers
[n
]->GetIcon(mimetype
, icon
) )
1597 wxFileTypeImpl::GetExpandedCommand(wxString
*expandedCmd
,
1598 const wxFileType::MessageParameters
& params
,
1601 MailCapEntry
*entry
= GetEntry(params
);
1602 if ( entry
== NULL
) {
1603 // all tests failed...
1607 wxString cmd
= open
? entry
->GetOpenCmd() : entry
->GetPrintCmd();
1608 if ( cmd
.IsEmpty() ) {
1609 // may happen, especially for "print"
1613 *expandedCmd
= wxFileType::ExpandCommand(cmd
, params
);
1617 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
1619 wxString strExtensions
= m_manager
->GetExtension(m_index
);
1622 // one extension in the space or comma delimitid list
1624 for ( const wxChar
*p
= strExtensions
; ; p
++ ) {
1625 if ( *p
== wxT(' ') || *p
== wxT(',') || *p
== wxT('\0') ) {
1626 if ( !strExt
.IsEmpty() ) {
1627 extensions
.Add(strExt
);
1630 //else: repeated spaces (shouldn't happen, but it's not that
1631 // important if it does happen)
1633 if ( *p
== wxT('\0') )
1636 else if ( *p
== wxT('.') ) {
1637 // remove the dot from extension (but only if it's the first char)
1638 if ( !strExt
.IsEmpty() ) {
1641 //else: no, don't append it
1651 // ----------------------------------------------------------------------------
1652 // wxMimeTypesManagerImpl (Unix)
1653 // ----------------------------------------------------------------------------
1656 ArrayIconHandlers
& wxMimeTypesManagerImpl::GetIconHandlers()
1658 if ( ms_iconHandlers
.GetCount() == 0 )
1660 ms_iconHandlers
.Add(&gs_iconHandlerGNOME
);
1661 ms_iconHandlers
.Add(&gs_iconHandlerKDE
);
1664 return ms_iconHandlers
;
1667 // read system and user mailcaps (TODO implement mime.types support)
1668 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
1670 // directories where we look for mailcap and mime.types by default
1671 // (taken from metamail(1) sources)
1672 static const wxChar
*aStandardLocations
[] =
1676 wxT("/usr/local/etc"),
1678 wxT("/usr/public/lib")
1681 // first read the system wide file(s)
1682 for ( size_t n
= 0; n
< WXSIZEOF(aStandardLocations
); n
++ ) {
1683 wxString dir
= aStandardLocations
[n
];
1685 wxString file
= dir
+ wxT("/mailcap");
1686 if ( wxFile::Exists(file
) ) {
1690 file
= dir
+ wxT("/mime.types");
1691 if ( wxFile::Exists(file
) ) {
1692 ReadMimeTypes(file
);
1696 wxString strHome
= wxGetenv(wxT("HOME"));
1698 // and now the users mailcap
1699 wxString strUserMailcap
= strHome
+ wxT("/.mailcap");
1700 if ( wxFile::Exists(strUserMailcap
) ) {
1701 ReadMailcap(strUserMailcap
);
1704 // read the users mime.types
1705 wxString strUserMimeTypes
= strHome
+ wxT("/.mime.types");
1706 if ( wxFile::Exists(strUserMimeTypes
) ) {
1707 ReadMimeTypes(strUserMimeTypes
);
1712 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
)
1714 size_t count
= m_aExtensions
.GetCount();
1715 for ( size_t n
= 0; n
< count
; n
++ ) {
1716 wxString extensions
= m_aExtensions
[n
];
1717 while ( !extensions
.IsEmpty() ) {
1718 wxString field
= extensions
.BeforeFirst(wxT(' '));
1719 extensions
= extensions
.AfterFirst(wxT(' '));
1721 // consider extensions as not being case-sensitive
1722 if ( field
.IsSameAs(ext
, FALSE
/* no case */) ) {
1724 wxFileType
*fileType
= new wxFileType
;
1725 fileType
->m_impl
->Init(this, n
);
1737 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
1739 // mime types are not case-sensitive
1740 wxString
mimetype(mimeType
);
1741 mimetype
.MakeLower();
1743 // first look for an exact match
1744 int index
= m_aTypes
.Index(mimetype
);
1745 if ( index
== wxNOT_FOUND
) {
1746 // then try to find "text/*" as match for "text/plain" (for example)
1747 // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
1748 // the whole string - ok.
1749 wxString strCategory
= mimetype
.BeforeFirst(wxT('/'));
1751 size_t nCount
= m_aTypes
.Count();
1752 for ( size_t n
= 0; n
< nCount
; n
++ ) {
1753 if ( (m_aTypes
[n
].BeforeFirst(wxT('/')) == strCategory
) &&
1754 m_aTypes
[n
].AfterFirst(wxT('/')) == wxT("*") ) {
1761 if ( index
!= wxNOT_FOUND
) {
1762 wxFileType
*fileType
= new wxFileType
;
1763 fileType
->m_impl
->Init(this, index
);
1773 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo
& filetype
)
1775 wxString extensions
;
1776 const wxArrayString
& exts
= filetype
.GetExtensions();
1777 size_t nExts
= exts
.GetCount();
1778 for ( size_t nExt
= 0; nExt
< nExts
; nExt
++ ) {
1780 extensions
+= wxT(' ');
1782 extensions
+= exts
[nExt
];
1785 AddMimeTypeInfo(filetype
.GetMimeType(),
1787 filetype
.GetDescription());
1789 AddMailcapInfo(filetype
.GetMimeType(),
1790 filetype
.GetOpenCommand(),
1791 filetype
.GetPrintCommand(),
1793 filetype
.GetDescription());
1796 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString
& strMimeType
,
1797 const wxString
& strExtensions
,
1798 const wxString
& strDesc
)
1800 int index
= m_aTypes
.Index(strMimeType
);
1801 if ( index
== wxNOT_FOUND
) {
1803 m_aTypes
.Add(strMimeType
);
1804 m_aEntries
.Add(NULL
);
1805 m_aExtensions
.Add(strExtensions
);
1806 m_aDescriptions
.Add(strDesc
);
1809 // modify an existing one
1810 if ( !strDesc
.IsEmpty() ) {
1811 m_aDescriptions
[index
] = strDesc
; // replace old value
1813 m_aExtensions
[index
] += ' ' + strExtensions
;
1817 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString
& strType
,
1818 const wxString
& strOpenCmd
,
1819 const wxString
& strPrintCmd
,
1820 const wxString
& strTest
,
1821 const wxString
& strDesc
)
1823 MailCapEntry
*entry
= new MailCapEntry(strOpenCmd
, strPrintCmd
, strTest
);
1825 int nIndex
= m_aTypes
.Index(strType
);
1826 if ( nIndex
== wxNOT_FOUND
) {
1828 m_aTypes
.Add(strType
);
1830 m_aEntries
.Add(entry
);
1831 m_aExtensions
.Add(wxT(""));
1832 m_aDescriptions
.Add(strDesc
);
1835 // always append the entry in the tail of the list - info added with
1836 // this function can only come from AddFallbacks()
1837 MailCapEntry
*entryOld
= m_aEntries
[nIndex
];
1839 entry
->Append(entryOld
);
1841 m_aEntries
[nIndex
] = entry
;
1845 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString
& strFileName
)
1847 wxLogTrace(wxT("--- Parsing mime.types file '%s' ---"), strFileName
.c_str());
1849 wxTextFile
file(strFileName
);
1853 // the information we extract
1854 wxString strMimeType
, strDesc
, strExtensions
;
1856 size_t nLineCount
= file
.GetLineCount();
1857 const wxChar
*pc
= NULL
;
1858 for ( size_t nLine
= 0; nLine
< nLineCount
; nLine
++ ) {
1860 // now we're at the start of the line
1861 pc
= file
[nLine
].c_str();
1864 // we didn't finish with the previous line yet
1869 while ( wxIsspace(*pc
) )
1872 // comment or blank line?
1873 if ( *pc
== wxT('#') || !*pc
) {
1874 // skip the whole line
1879 // detect file format
1880 const wxChar
*pEqualSign
= wxStrchr(pc
, wxT('='));
1881 if ( pEqualSign
== NULL
) {
1885 // first field is mime type
1886 for ( strMimeType
.Empty(); !wxIsspace(*pc
) && *pc
!= wxT('\0'); pc
++ ) {
1891 while ( wxIsspace(*pc
) )
1894 // take all the rest of the string
1897 // no description...
1904 // the string on the left of '=' is the field name
1905 wxString
strLHS(pc
, pEqualSign
- pc
);
1908 for ( pc
= pEqualSign
+ 1; wxIsspace(*pc
); pc
++ )
1912 if ( *pc
== wxT('"') ) {
1913 // the string is quoted and ends at the matching quote
1914 pEnd
= wxStrchr(++pc
, wxT('"'));
1915 if ( pEnd
== NULL
) {
1916 wxLogWarning(_("Mime.types file %s, line %d: unterminated "
1918 strFileName
.c_str(), nLine
+ 1);
1922 // unquoted string ends at the first space
1923 for ( pEnd
= pc
; !wxIsspace(*pEnd
); pEnd
++ )
1927 // now we have the RHS (field value)
1928 wxString
strRHS(pc
, pEnd
- pc
);
1930 // check what follows this entry
1931 if ( *pEnd
== wxT('"') ) {
1936 for ( pc
= pEnd
; wxIsspace(*pc
); pc
++ )
1939 // if there is something left, it may be either a '\\' to continue
1940 // the line or the next field of the same entry
1941 bool entryEnded
= *pc
== wxT('\0'),
1942 nextFieldOnSameLine
= FALSE
;
1943 if ( !entryEnded
) {
1944 nextFieldOnSameLine
= ((*pc
!= wxT('\\')) || (pc
[1] != wxT('\0')));
1947 // now see what we got
1948 if ( strLHS
== wxT("type") ) {
1949 strMimeType
= strRHS
;
1951 else if ( strLHS
== wxT("desc") ) {
1954 else if ( strLHS
== wxT("exts") ) {
1955 strExtensions
= strRHS
;
1958 wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
1959 strFileName
.c_str(), nLine
+ 1, strLHS
.c_str());
1962 if ( !entryEnded
) {
1963 if ( !nextFieldOnSameLine
)
1965 //else: don't reset it
1967 // as we don't reset strMimeType, the next field in this entry
1968 // will be interpreted correctly.
1974 // although it doesn't seem to be covered by RFCs, some programs
1975 // (notably Netscape) create their entries with several comma
1976 // separated extensions (RFC mention the spaces only)
1977 strExtensions
.Replace(wxT(","), wxT(" "));
1979 // also deal with the leading dot
1980 if ( !strExtensions
.IsEmpty() && strExtensions
[0u] == wxT('.') )
1982 strExtensions
.erase(0, 1);
1985 AddMimeTypeInfo(strMimeType
, strExtensions
, strDesc
);
1987 // finished with this line
1991 // check our data integriry
1992 wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() &&
1993 m_aTypes
.Count() == m_aExtensions
.Count() &&
1994 m_aTypes
.Count() == m_aDescriptions
.Count() );
1999 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString
& strFileName
,
2002 wxLogTrace(wxT("--- Parsing mailcap file '%s' ---"), strFileName
.c_str());
2004 wxTextFile
file(strFileName
);
2008 // see the comments near the end of function for the reason we need these
2009 // variables (search for the next occurence of them)
2010 // indices of MIME types (in m_aTypes) we already found in this file
2011 wxArrayInt aEntryIndices
;
2012 // aLastIndices[n] is the index of last element in
2013 // m_aEntries[aEntryIndices[n]] from this file
2014 wxArrayInt aLastIndices
;
2016 size_t nLineCount
= file
.GetLineCount();
2017 for ( size_t nLine
= 0; nLine
< nLineCount
; nLine
++ ) {
2018 // now we're at the start of the line
2019 const wxChar
*pc
= file
[nLine
].c_str();
2022 while ( wxIsspace(*pc
) )
2025 // comment or empty string?
2026 if ( *pc
== wxT('#') || *pc
== wxT('\0') )
2031 // what field are we currently in? The first 2 are fixed and there may
2032 // be an arbitrary number of other fields -- currently, we are not
2033 // interested in any of them, but we should parse them as well...
2039 } currentToken
= Field_Type
;
2041 // the flags and field values on the current line
2042 bool needsterminal
= FALSE
,
2043 copiousoutput
= FALSE
;
2049 curField
; // accumulator
2050 for ( bool cont
= TRUE
; cont
; pc
++ ) {
2053 // interpret the next character literally (notice that
2054 // backslash can be used for line continuation)
2055 if ( *++pc
== wxT('\0') ) {
2056 // fetch the next line.
2058 // pc currently points to nowhere, but after the next
2059 // pc++ in the for line it will point to the beginning
2060 // of the next line in the file
2061 pc
= file
[++nLine
].c_str() - 1;
2064 // just a normal character
2070 cont
= FALSE
; // end of line reached, exit the loop
2075 // store this field and start looking for the next one
2077 // trim whitespaces from both sides
2078 curField
.Trim(TRUE
).Trim(FALSE
);
2080 switch ( currentToken
) {
2083 if ( strType
.Find(wxT('/')) == wxNOT_FOUND
) {
2084 // we interpret "type" as "type/*"
2085 strType
+= wxT("/*");
2088 currentToken
= Field_OpenCmd
;
2092 strOpenCmd
= curField
;
2094 currentToken
= Field_Other
;
2099 // "good" mailcap entry?
2102 // is this something of the form foo=bar?
2103 const wxChar
*pEq
= wxStrchr(curField
, wxT('='));
2104 if ( pEq
!= NULL
) {
2105 wxString lhs
= curField
.BeforeFirst(wxT('=')),
2106 rhs
= curField
.AfterFirst(wxT('='));
2108 lhs
.Trim(TRUE
); // from right
2109 rhs
.Trim(FALSE
); // from left
2111 if ( lhs
== wxT("print") )
2113 else if ( lhs
== wxT("test") )
2115 else if ( lhs
== wxT("description") ) {
2116 // it might be quoted
2117 if ( rhs
[0u] == wxT('"') &&
2118 rhs
.Last() == wxT('"') ) {
2119 strDesc
= wxString(rhs
.c_str() + 1,
2126 else if ( lhs
== wxT("compose") ||
2127 lhs
== wxT("composetyped") ||
2128 lhs
== wxT("notes") ||
2129 lhs
== wxT("edit") )
2136 // no, it's a simple flag
2137 // TODO support the flags:
2138 // 1. create an xterm for 'needsterminal'
2139 // 2. append "| $PAGER" for 'copiousoutput'
2140 if ( curField
== wxT("needsterminal") )
2141 needsterminal
= TRUE
;
2142 else if ( curField
== wxT("copiousoutput") )
2143 copiousoutput
= TRUE
;
2144 else if ( curField
== wxT("textualnewlines") )
2152 // we don't understand this field, but
2153 // Netscape stores info in it, so don't warn
2155 if ( curField
.Left(16u) != "x-mozilla-flags=" )
2157 // don't flood the user with error
2158 // messages if we don't understand
2159 // something in his mailcap, but give
2160 // them in debug mode because this might
2161 // be useful for the programmer
2164 wxT("Mailcap file %s, line %d: "
2165 "unknown field '%s' for the "
2166 "MIME type '%s' ignored."),
2167 strFileName
.c_str(),
2176 // it already has this value
2177 //currentToken = Field_Other;
2181 wxFAIL_MSG(wxT("unknown field type in mailcap"));
2184 // next token starts immediately after ';'
2193 // check that we really read something reasonable
2194 if ( currentToken
== Field_Type
|| currentToken
== Field_OpenCmd
) {
2195 wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
2197 strFileName
.c_str(), nLine
+ 1);
2200 MailCapEntry
*entry
= new MailCapEntry(strOpenCmd
,
2204 // NB: because of complications below (we must get entries priority
2205 // right), we can't use AddMailcapInfo() here, unfortunately.
2206 strType
.MakeLower();
2207 int nIndex
= m_aTypes
.Index(strType
);
2208 if ( nIndex
== wxNOT_FOUND
) {
2210 m_aTypes
.Add(strType
);
2212 m_aEntries
.Add(entry
);
2213 m_aExtensions
.Add(wxT(""));
2214 m_aDescriptions
.Add(strDesc
);
2217 // modify the existing entry: the entries in one and the same
2218 // file are read in top-to-bottom order, i.e. the entries read
2219 // first should be tried before the entries below. However,
2220 // the files read later should override the settings in the
2221 // files read before (except if fallback is TRUE), thus we
2222 // Insert() the new entry to the list if it has already
2223 // occured in _this_ file, but Prepend() it if it occured in
2224 // some of the previous ones and Append() to it in the
2228 // 'fallback' parameter prevents the entries from this
2229 // file from overriding the other ones - always append
2230 MailCapEntry
*entryOld
= m_aEntries
[nIndex
];
2232 entry
->Append(entryOld
);
2234 m_aEntries
[nIndex
] = entry
;
2237 int entryIndex
= aEntryIndices
.Index(nIndex
);
2238 if ( entryIndex
== wxNOT_FOUND
) {
2239 // first time in this file
2240 aEntryIndices
.Add(nIndex
);
2241 aLastIndices
.Add(0);
2243 entry
->Prepend(m_aEntries
[nIndex
]);
2244 m_aEntries
[nIndex
] = entry
;
2247 // not the first time in _this_ file
2248 size_t nEntryIndex
= (size_t)entryIndex
;
2249 MailCapEntry
*entryOld
= m_aEntries
[nIndex
];
2251 entry
->Insert(entryOld
, aLastIndices
[nEntryIndex
]);
2253 m_aEntries
[nIndex
] = entry
;
2255 // the indices were shifted by 1
2256 aLastIndices
[nEntryIndex
]++;
2260 if ( !strDesc
.IsEmpty() ) {
2261 // replace the old one - what else can we do??
2262 m_aDescriptions
[nIndex
] = strDesc
;
2267 // check our data integriry
2268 wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() &&
2269 m_aTypes
.Count() == m_aExtensions
.Count() &&
2270 m_aTypes
.Count() == m_aDescriptions
.Count() );
2276 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
2281 size_t count
= m_aTypes
.GetCount();
2282 for ( size_t n
= 0; n
< count
; n
++ )
2284 // don't return template types from here (i.e. anything containg '*')
2286 if ( type
.Find(_T('*')) == wxNOT_FOUND
)
2288 mimetypes
.Add(type
);
2292 return mimetypes
.GetCount();
2299 // wxUSE_FILE && wxUSE_TEXTFILE