]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/mimetype.cpp
wxMenuItem::GetLabelFromText() added/documented, bug in wxMenu::FindItem() fixed
[wxWidgets.git] / src / common / mimetype.cpp
index 513c35b3fc55598bf995591537ae626186ed86f4..3dab4f75fae31d8568db2add17e2d3b0b7cc1154 100644 (file)
 /////////////////////////////////////////////////////////////////////////////
 
 #ifdef    __GNUG__
 /////////////////////////////////////////////////////////////////////////////
 
 #ifdef    __GNUG__
-    #pragma implementation "mimetype.h"
+#pragma implementation "mimetype.h"
 #endif
 
 #endif
 
-// ============================================================================
-// declarations
-// ============================================================================
-
-// ----------------------------------------------------------------------------
-// headers
-// ----------------------------------------------------------------------------
-
 // for compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
 // for compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-    #pragma hdrstop
+  #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+  #include "wx/defs.h"
 #endif
 
 #endif
 
-// wxWindows
+#if (wxUSE_FILE && wxUSE_TEXTFILE) || defined(__WXMSW__)
+
 #ifndef WX_PRECOMP
 #ifndef WX_PRECOMP
-    #include  "wx/string.h"
-    #include  "wx/icon.h"
+  #include "wx/string.h"
+  #if wxUSE_GUI
+    #include "wx/icon.h"
+  #endif
 #endif //WX_PRECOMP
 
 // Doesn't compile in WIN16 mode
 #ifndef __WIN16__
 
 #include "wx/log.h"
 #endif //WX_PRECOMP
 
 // Doesn't compile in WIN16 mode
 #ifndef __WIN16__
 
 #include "wx/log.h"
+#include "wx/file.h"
 #include "wx/intl.h"
 #include "wx/dynarray.h"
 #include "wx/confbase.h"
 #include "wx/intl.h"
 #include "wx/dynarray.h"
 #include "wx/confbase.h"
 #ifdef __WXMSW__
     #include "wx/msw/registry.h"
     #include "windows.h"
 #ifdef __WXMSW__
     #include "wx/msw/registry.h"
     #include "windows.h"
-#else  // Unix
+#elif defined(__UNIX__)  || defined(__WXPM__)
+    #include "wx/ffile.h"
     #include "wx/textfile.h"
     #include "wx/textfile.h"
+    #include "wx/dir.h"
+    #include "wx/utils.h"
 #endif // OS
 
 #include "wx/mimetype.h"
 #endif // OS
 
 #include "wx/mimetype.h"
@@ -54,6 +57,9 @@
 // other standard headers
 #include <ctype.h>
 
 // other standard headers
 #include <ctype.h>
 
+// in case we're compiling in non-GUI mode
+class WXDLLEXPORT wxIcon;
+
 // ----------------------------------------------------------------------------
 // private classes
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // private classes
 // ----------------------------------------------------------------------------
 //      to open/print the file (the positional parameters are introduced by %1,
 //      %2, ... in these strings, we change them to %s ourselves)
 
 //      to open/print the file (the positional parameters are introduced by %1,
 //      %2, ... in these strings, we change them to %s ourselves)
 
+// although I don't know of any official documentation which mentions this
+// location, uses it, so it isn't likely to change
+static const wxChar *MIME_DATABASE_KEY = wxT("MIME\\Database\\Content Type\\");
+
 class wxFileTypeImpl
 {
 public:
     // ctor
 class wxFileTypeImpl
 {
 public:
     // ctor
-    wxFileTypeImpl() { }
+    wxFileTypeImpl() { m_info = NULL; }
 
 
-    // initialize us with our file type name
-    void SetFileType(const wxString& strFileType)
-        { m_strFileType = strFileType; }
-    void SetExt(const wxString& ext)
-        { m_ext = ext; }
+    // one of these Init() function must be called (ctor can't take any
+    // arguments because it's common)
+
+        // initialize us with our file type name and extension - in this case
+        // we will read all other data from the registry
+    void Init(const wxString& strFileType, const wxString& ext)
+        { m_strFileType = strFileType; m_ext = ext; }
+
+        // initialize us with a wxFileTypeInfo object - it contains all the
+        // data
+    void Init(const wxFileTypeInfo& info)
+        { m_info = &info; }
 
     // implement accessor functions
     bool GetExtensions(wxArrayString& extensions);
 
     // implement accessor functions
     bool GetExtensions(wxArrayString& extensions);
@@ -99,19 +116,25 @@ public:
     bool GetIcon(wxIcon *icon) const;
     bool GetDescription(wxString *desc) const;
     bool GetOpenCommand(wxString *openCmd,
     bool GetIcon(wxIcon *icon) const;
     bool GetDescription(wxString *desc) const;
     bool GetOpenCommand(wxString *openCmd,
-                        const wxFileType::MessageParameters&) const
-        { return GetCommand(openCmd, "open"); }
+                        const wxFileType::MessageParameters& params) const;
     bool GetPrintCommand(wxString *printCmd,
     bool GetPrintCommand(wxString *printCmd,
-                         const wxFileType::MessageParameters&) const
-        { return GetCommand(printCmd, "print"); }
+                         const wxFileType::MessageParameters& params) const;
 
 private:
 
 private:
-    // helper function
-    bool GetCommand(wxString *command, const char *verb) const;
-
-    wxString m_strFileType, m_ext;
+    // helper function: reads the command corresponding to the specified verb
+    // from the registry (returns an empty string if not found)
+    wxString GetCommand(const wxChar *verb) const;
+
+    // we use either m_info or read the data from the registry if m_info == NULL
+    const wxFileTypeInfo *m_info;
+    wxString m_strFileType,         // may be empty
+             m_ext;
 };
 
 };
 
+WX_DECLARE_EXPORTED_OBJARRAY(wxFileTypeInfo, wxArrayFileTypeInfo);
+#include "wx/arrimpl.cpp"
+WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo);
+
 class wxMimeTypesManagerImpl
 {
 public:
 class wxMimeTypesManagerImpl
 {
 public:
@@ -123,9 +146,73 @@ public:
     wxFileType *GetFileTypeFromExtension(const wxString& ext);
     wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
 
     wxFileType *GetFileTypeFromExtension(const wxString& ext);
     wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
 
+    size_t EnumAllFileTypes(wxArrayString& mimetypes);
+
     // this are NOPs under Windows
     // this are NOPs under Windows
-    void ReadMailcap(const wxString& filename) { }
-    void ReadMimeTypes(const wxString& filename) { }
+    bool ReadMailcap(const wxString& filename, bool fallback = TRUE)
+        { return TRUE; }
+    bool ReadMimeTypes(const wxString& filename)
+        { return TRUE; }
+
+    void AddFallback(const wxFileTypeInfo& ft) { m_fallbacks.Add(ft); }
+
+private:
+    wxArrayFileTypeInfo m_fallbacks;
+};
+
+#elif defined( __WXMAC__ )
+
+WX_DECLARE_EXPORTED_OBJARRAY(wxFileTypeInfo, wxArrayFileTypeInfo);
+#include "wx/arrimpl.cpp"
+WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo);
+
+class wxMimeTypesManagerImpl
+{
+public :
+    wxMimeTypesManagerImpl() { }
+
+    // implement containing class functions
+    wxFileType *GetFileTypeFromExtension(const wxString& ext);
+    wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
+
+    size_t EnumAllFileTypes(wxArrayString& mimetypes);
+
+    // this are NOPs under MacOS
+    bool ReadMailcap(const wxString& filename, bool fallback = TRUE) { return TRUE; }
+    bool ReadMimeTypes(const wxString& filename) { return TRUE; }
+
+    void AddFallback(const wxFileTypeInfo& ft) { m_fallbacks.Add(ft); }
+
+private:
+    wxArrayFileTypeInfo m_fallbacks;
+};
+
+class wxFileTypeImpl
+{
+public:
+    // initialize us with our file type name
+    void SetFileType(const wxString& strFileType)
+        { m_strFileType = strFileType; }
+    void SetExt(const wxString& ext)
+        { m_ext = ext; }
+
+    // implement accessor functions
+    bool GetExtensions(wxArrayString& extensions);
+    bool GetMimeType(wxString *mimeType) const;
+    bool GetIcon(wxIcon *icon) const;
+    bool GetDescription(wxString *desc) const;
+    bool GetOpenCommand(wxString *openCmd,
+                        const wxFileType::MessageParameters&) const
+        { return GetCommand(openCmd, "open"); }
+    bool GetPrintCommand(wxString *printCmd,
+                         const wxFileType::MessageParameters&) const
+        { return GetCommand(printCmd, "print"); }
+
+private:
+    // helper function
+    bool GetCommand(wxString *command, const char *verb) const;
+
+    wxString m_strFileType, m_ext;
 };
 
 #else  // Unix
 };
 
 #else  // Unix
@@ -170,7 +257,7 @@ public:
 //  * compose and composetyped fields are used to determine the program to be
 //    called to create a new message pert in the specified format (unused).
 //
 //  * compose and composetyped fields are used to determine the program to be
 //    called to create a new message pert in the specified format (unused).
 //
-// Parameter/filename xpansion: 
+// Parameter/filename xpansion:
 //  * %s is replaced with the (full) file name
 //  * %t is replaced with MIME type/subtype of the entry
 //  * for multipart type only %n is replaced with the nnumber of parts and %F is
 //  * %s is replaced with the (full) file name
 //  * %t is replaced with MIME type/subtype of the entry
 //  * for multipart type only %n is replaced with the nnumber of parts and %F is
@@ -189,7 +276,7 @@ public:
 // comments. Each record has one of two following forms:
 //  a) for "brief" format:
 //      <mime type>  <space separated list of extensions>
 // comments. Each record has one of two following forms:
 //  a) for "brief" format:
 //      <mime type>  <space separated list of extensions>
-//  b) for "expanded" format: 
+//  b) for "expanded" format:
 //      type=<mime type> \ desc="<description>" \ exts="ext"
 //
 // We try to autodetect the format of mime.types: if a non-comment line starts
 //      type=<mime type> \ desc="<description>" \ exts="ext"
 //
 // We try to autodetect the format of mime.types: if a non-comment line starts
@@ -220,9 +307,27 @@ public:
     // operations
         // prepend this element to the list
     void Prepend(MailCapEntry *next) { m_next = next; }
     // operations
         // prepend this element to the list
     void Prepend(MailCapEntry *next) { m_next = next; }
-        // append to the list
+        // insert into the list at given position
+    void Insert(MailCapEntry *next, size_t pos)
+    {
+        // FIXME slooow...
+        MailCapEntry *cur;
+        size_t n = 0;
+        for ( cur = next; cur != NULL; cur = cur->m_next, n++ ) {
+            if ( n == pos )
+                break;
+        }
+
+        wxASSERT_MSG( n == pos, wxT("invalid position in MailCapEntry::Insert") );
+
+        m_next = cur->m_next;
+        cur->m_next = this;
+    }
+        // append this element to the list
     void Append(MailCapEntry *next)
     {
     void Append(MailCapEntry *next)
     {
+        wxCHECK_RET( next != NULL, wxT("Append()ing to what?") );
+
         // FIXME slooow...
         MailCapEntry *cur;
         for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
         // FIXME slooow...
         MailCapEntry *cur;
         for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
@@ -230,9 +335,7 @@ public:
 
         cur->m_next = this;
 
 
         cur->m_next = this;
 
-        // we initialize it in the ctor and there is no reason to both Prepend()
-        // and Append() one and the same entry
-        wxASSERT( m_next == NULL );
+        wxASSERT_MSG( !m_next, wxT("Append()ing element already in the list?") );
     }
 
 private:
     }
 
 private:
@@ -246,32 +349,100 @@ private:
 
 WX_DEFINE_ARRAY(MailCapEntry *, ArrayTypeEntries);
 
 
 WX_DEFINE_ARRAY(MailCapEntry *, ArrayTypeEntries);
 
+// the base class which may be used to find an icon for the MIME type
+class wxMimeTypeIconHandler
+{
+public:
+    virtual bool GetIcon(const wxString& mimetype, wxIcon *icon) = 0;
+};
+
+WX_DEFINE_ARRAY(wxMimeTypeIconHandler *, ArrayIconHandlers);
+
+// the icon handler which uses GNOME MIME database
+class wxGNOMEIconHandler : public wxMimeTypeIconHandler
+{
+public:
+    virtual bool GetIcon(const wxString& mimetype, wxIcon *icon);
+
+private:
+    void Init();
+    void LoadIconsFromKeyFile(const wxString& filename);
+    void LoadKeyFilesFromDir(const wxString& dirbase);
+
+    static bool m_inited;
+
+    static wxSortedArrayString ms_mimetypes;
+    static wxArrayString       ms_icons;
+};
+
+// the icon handler which uses KDE MIME database
+class wxKDEIconHandler : public wxMimeTypeIconHandler
+{
+public:
+    virtual bool GetIcon(const wxString& mimetype, wxIcon *icon);
+
+private:
+    void LoadLinksForMimeSubtype(const wxString& dirbase,
+                                 const wxString& subdir,
+                                 const wxString& filename);
+    void LoadLinksForMimeType(const wxString& dirbase,
+                              const wxString& subdir);
+    void LoadLinkFilesFromDir(const wxString& dirbase);
+    void Init();
+
+    static bool m_inited;
+
+    static wxSortedArrayString ms_mimetypes;
+    static wxArrayString       ms_icons;
+};
+
+// this is the real wxMimeTypesManager for Unix
 class wxMimeTypesManagerImpl
 {
 friend class wxFileTypeImpl; // give it access to m_aXXX variables
 
 public:
     // ctor loads all info into memory for quicker access later on
 class wxMimeTypesManagerImpl
 {
 friend class wxFileTypeImpl; // give it access to m_aXXX variables
 
 public:
     // ctor loads all info into memory for quicker access later on
-    // @@ it would be nice to load them all, but parse on demand only...
+    // TODO it would be nice to load them all, but parse on demand only...
     wxMimeTypesManagerImpl();
 
     // implement containing class functions
     wxFileType *GetFileTypeFromExtension(const wxString& ext);
     wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
 
     wxMimeTypesManagerImpl();
 
     // implement containing class functions
     wxFileType *GetFileTypeFromExtension(const wxString& ext);
     wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
 
-    void ReadMailcap(const wxString& filename);
-    void ReadMimeTypes(const wxString& filename);
+    size_t EnumAllFileTypes(wxArrayString& mimetypes);
+
+    bool ReadMailcap(const wxString& filename, bool fallback = FALSE);
+    bool ReadMimeTypes(const wxString& filename);
+
+    void AddFallback(const wxFileTypeInfo& filetype);
+
+    // add information about the given mimetype
+    void AddMimeTypeInfo(const wxString& mimetype,
+                         const wxString& extensions,
+                         const wxString& description);
+    void AddMailcapInfo(const wxString& strType,
+                        const wxString& strOpenCmd,
+                        const wxString& strPrintCmd,
+                        const wxString& strTest,
+                        const wxString& strDesc);
 
     // accessors
         // get the string containing space separated extensions for the given
         // file type
     wxString GetExtension(size_t index) { return m_aExtensions[index]; }
 
 
     // accessors
         // get the string containing space separated extensions for the given
         // file type
     wxString GetExtension(size_t index) { return m_aExtensions[index]; }
 
+        // get the array of icon handlers
+    static ArrayIconHandlers& GetIconHandlers();
+
 private:
     wxArrayString m_aTypes,         // MIME types
                   m_aDescriptions,  // descriptions (just some text)
                   m_aExtensions;    // space separated list of extensions
     ArrayTypeEntries m_aEntries;    // commands and tests for this file type
 private:
     wxArrayString m_aTypes,         // MIME types
                   m_aDescriptions,  // descriptions (just some text)
                   m_aExtensions;    // space separated list of extensions
     ArrayTypeEntries m_aEntries;    // commands and tests for this file type
+
+    // head of the linked list of the icon handlers
+    static ArrayIconHandlers ms_iconHandlers;
 };
 
 class wxFileTypeImpl
 };
 
 class wxFileTypeImpl
@@ -285,8 +456,7 @@ public:
     bool GetExtensions(wxArrayString& extensions);
     bool GetMimeType(wxString *mimeType) const
         { *mimeType = m_manager->m_aTypes[m_index]; return TRUE; }
     bool GetExtensions(wxArrayString& extensions);
     bool GetMimeType(wxString *mimeType) const
         { *mimeType = m_manager->m_aTypes[m_index]; return TRUE; }
-    bool GetIcon(wxIcon *icon) const
-        { return FALSE; }   // @@ maybe with Gnome/KDE integration...
+    bool GetIcon(wxIcon *icon) const;
     bool GetDescription(wxString *desc) const
         { *desc = m_manager->m_aDescriptions[m_index]; return TRUE; }
 
     bool GetDescription(wxString *desc) const
         { *desc = m_manager->m_aDescriptions[m_index]; return TRUE; }
 
@@ -317,6 +487,42 @@ private:
 
 #endif // OS type
 
 
 #endif // OS type
 
+// ============================================================================
+// common classes
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// wxFileTypeInfo
+// ----------------------------------------------------------------------------
+
+wxFileTypeInfo::wxFileTypeInfo(const char *mimeType,
+                               const char *openCmd,
+                               const char *printCmd,
+                               const char *desc,
+                               ...)
+              : m_mimeType(mimeType),
+                m_openCmd(openCmd),
+                m_printCmd(printCmd),
+                m_desc(desc)
+{
+    va_list argptr;
+    va_start(argptr, desc);
+
+    for ( ;; )
+    {
+        const char *ext = va_arg(argptr, const char *);
+        if ( !ext )
+        {
+            // NULL terminates the list
+            break;
+        }
+
+        m_exts.Add(ext);
+    }
+
+    va_end(argptr);
+}
+
 // ============================================================================
 // implementation of the wrapper classes
 // ============================================================================
 // ============================================================================
 // implementation of the wrapper classes
 // ============================================================================
@@ -331,57 +537,57 @@ wxString wxFileType::ExpandCommand(const wxString& command,
     bool hasFilename = FALSE;
 
     wxString str;
     bool hasFilename = FALSE;
 
     wxString str;
-    for ( const char *pc = command.c_str(); *pc != '\0'; pc++ ) {
-        if ( *pc == '%' ) {
+    for ( const wxChar *pc = command.c_str(); *pc != wxT('\0'); pc++ ) {
+        if ( *pc == wxT('%') ) {
             switch ( *++pc ) {
             switch ( *++pc ) {
-                case 's':
+                case wxT('s'):
                     // '%s' expands into file name (quoted because it might
                     // contain spaces) - except if there are already quotes
                     // '%s' expands into file name (quoted because it might
                     // contain spaces) - except if there are already quotes
-                    // there because otherwise some programs may get confused by
-                    // double double quotes
+                    // there because otherwise some programs may get confused
+                    // by double double quotes
 #if 0
 #if 0
-                    if ( *(pc - 2) == '"' )
+                    if ( *(pc - 2) == wxT('"') )
                         str << params.GetFileName();
                     else
                         str << params.GetFileName();
                     else
-                        str << '"' << params.GetFileName() << '"';
+                        str << wxT('"') << params.GetFileName() << wxT('"');
 #endif
                     str << params.GetFileName();
                     hasFilename = TRUE;
                     break;
 
 #endif
                     str << params.GetFileName();
                     hasFilename = TRUE;
                     break;
 
-                case 't':
+                case wxT('t'):
                     // '%t' expands into MIME type (quote it too just to be
                     // consistent)
                     // '%t' expands into MIME type (quote it too just to be
                     // consistent)
-                    str << '\'' << params.GetMimeType() << '\'';
+                    str << wxT('\'') << params.GetMimeType() << wxT('\'');
                     break;
 
                     break;
 
-                case '{':
+                case wxT('{'):
                     {
                     {
-                        const char *pEnd = strchr(pc, '}');
+                        const wxChar *pEnd = wxStrchr(pc, wxT('}'));
                         if ( pEnd == NULL ) {
                             wxString mimetype;
                             wxLogWarning(_("Unmatched '{' in an entry for "
                                            "mime type %s."),
                                          params.GetMimeType().c_str());
                         if ( pEnd == NULL ) {
                             wxString mimetype;
                             wxLogWarning(_("Unmatched '{' in an entry for "
                                            "mime type %s."),
                                          params.GetMimeType().c_str());
-                            str << "%{";
+                            str << wxT("%{");
                         }
                         else {
                             wxString param(pc + 1, pEnd - pc - 1);
                         }
                         else {
                             wxString param(pc + 1, pEnd - pc - 1);
-                            str << '\'' << params.GetParamValue(param) << '\'';
+                            str << wxT('\'') << params.GetParamValue(param) << wxT('\'');
                             pc = pEnd;
                         }
                     }
                     break;
 
                             pc = pEnd;
                         }
                     }
                     break;
 
-                case 'n':
-                case 'F':
+                case wxT('n'):
+                case wxT('F'):
                     // TODO %n is the number of parts, %F is an array containing
                     //      the names of temp files these parts were written to
                     //      and their mime types.
                     break;
 
                 default:
                     // TODO %n is the number of parts, %F is an array containing
                     //      the names of temp files these parts were written to
                     //      and their mime types.
                     break;
 
                 default:
-                    wxLogDebug("Unknown field %%%c in command '%s'.",
+                    wxLogDebug(wxT("Unknown field %%%c in command '%s'."),
                                *pc, command.c_str());
                     str << *pc;
             }
                                *pc, command.c_str());
                     str << *pc;
             }
@@ -394,7 +600,7 @@ wxString wxFileType::ExpandCommand(const wxString& command,
     // metamail(1) man page states that if the mailcap entry doesn't have '%s'
     // the program will accept the data on stdin: so give it to it!
     if ( !hasFilename && !str.IsEmpty() ) {
     // metamail(1) man page states that if the mailcap entry doesn't have '%s'
     // the program will accept the data on stdin: so give it to it!
     if ( !hasFilename && !str.IsEmpty() ) {
-        str << " < '" << params.GetFileName() << '\'';
+        str << wxT(" < '") << params.GetFileName() << wxT('\'');
     }
 
     return str;
     }
 
     return str;
@@ -448,6 +654,28 @@ wxFileType::GetPrintCommand(wxString *printCmd,
 // wxMimeTypesManager
 // ----------------------------------------------------------------------------
 
 // wxMimeTypesManager
 // ----------------------------------------------------------------------------
 
+bool wxMimeTypesManager::IsOfType(const wxString& mimeType,
+                                  const wxString& wildcard)
+{
+    wxASSERT_MSG( mimeType.Find(wxT('*')) == wxNOT_FOUND,
+                  wxT("first MIME type can't contain wildcards") );
+
+    // all comparaisons are case insensitive (2nd arg of IsSameAs() is FALSE)
+    if ( wildcard.BeforeFirst(wxT('/')).IsSameAs(mimeType.BeforeFirst(wxT('/')), FALSE) )
+    {
+        wxString strSubtype = wildcard.AfterFirst(wxT('/'));
+
+        if ( strSubtype == wxT("*") ||
+             strSubtype.IsSameAs(mimeType.AfterFirst(wxT('/')), FALSE) )
+        {
+            // matches (either exactly or it's a wildcard)
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
 wxMimeTypesManager::wxMimeTypesManager()
 {
     m_impl = new wxMimeTypesManagerImpl;
 wxMimeTypesManager::wxMimeTypesManager()
 {
     m_impl = new wxMimeTypesManagerImpl;
@@ -470,54 +698,131 @@ wxMimeTypesManager::GetFileTypeFromMimeType(const wxString& mimeType)
     return m_impl->GetFileTypeFromMimeType(mimeType);
 }
 
     return m_impl->GetFileTypeFromMimeType(mimeType);
 }
 
+bool wxMimeTypesManager::ReadMailcap(const wxString& filename, bool fallback)
+{
+    return m_impl->ReadMailcap(filename, fallback);
+}
+
+bool wxMimeTypesManager::ReadMimeTypes(const wxString& filename)
+{
+    return m_impl->ReadMimeTypes(filename);
+}
+
+void wxMimeTypesManager::AddFallbacks(const wxFileTypeInfo *filetypes)
+{
+    for ( const wxFileTypeInfo *ft = filetypes; ft->IsValid(); ft++ ) {
+        m_impl->AddFallback(*ft);
+    }
+}
+
+size_t wxMimeTypesManager::EnumAllFileTypes(wxArrayString& mimetypes)
+{
+    return m_impl->EnumAllFileTypes(mimetypes);
+}
+
 // ============================================================================
 // real (OS specific) implementation
 // ============================================================================
 
 #ifdef __WXMSW__
 
 // ============================================================================
 // real (OS specific) implementation
 // ============================================================================
 
 #ifdef __WXMSW__
 
-bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const
+wxString wxFileTypeImpl::GetCommand(const wxChar *verb) const
 {
     // suppress possible error messages
     wxLogNull nolog;
     wxString strKey;
 {
     // suppress possible error messages
     wxLogNull nolog;
     wxString strKey;
-    strKey << m_strFileType << "\\shell\\" << verb << "\\command";
-    wxRegKey key(wxRegKey::HKCR, strKey);
 
 
+    if ( wxRegKey(wxRegKey::HKCR, m_ext + _T("\\shell")).Exists() )
+        strKey = m_ext;
+    if ( wxRegKey(wxRegKey::HKCR, m_strFileType + _T("\\shell")).Exists() )
+        strKey = m_strFileType;
+
+    if ( !strKey )
+    {
+        // no info
+        return wxEmptyString;
+    }
+
+    strKey << wxT("\\shell\\") << verb << wxT("\\command");
+    wxRegKey key(wxRegKey::HKCR, strKey);
+    wxString command;
     if ( key.Open() ) {
         // it's the default value of the key
     if ( key.Open() ) {
         // it's the default value of the key
-        if ( key.QueryValue("", *command) ) {
+        if ( key.QueryValue(wxT(""), command) ) {
             // transform it from '%1' to '%s' style format string
             // transform it from '%1' to '%s' style format string
-            // @@ we don't make any attempt to verify that the string is valid,
-            //    i.e. doesn't contain %2, or second %1 or .... But we do make
-            //    sure that we return a string with _exactly_ one '%s'!
-            size_t len = command->Len();
-            for ( size_t n = 0; n < len; n++ ) {
-                if ( command->GetChar(n) == '%' &&
-                     (n + 1 < len) && command->GetChar(n + 1) == '1' ) {
+
+            // NB: we don't make any attempt to verify that the string is valid,
+            //     i.e. doesn't contain %2, or second %1 or .... But we do make
+            //     sure that we return a string with _exactly_ one '%s'!
+            bool foundFilename = FALSE;
+            size_t len = command.Len();
+            for ( size_t n = 0; (n < len) && !foundFilename; n++ ) {
+                if ( command[n] == wxT('%') &&
+                     (n + 1 < len) && command[n + 1] == wxT('1') ) {
                     // replace it with '%s'
                     // replace it with '%s'
-                    command->SetChar(n + 1, 's');
+                    command[n + 1] = wxT('s');
 
 
-                    return TRUE;
+                    foundFilename = TRUE;
                 }
             }
 
                 }
             }
 
-            // we didn't find any '%1'!
-            // @@@ hack: append the filename at the end, hope that it will do
-            *command << " %s";
-
-            return TRUE;
+            if ( !foundFilename ) {
+                // we didn't find any '%1'!
+                // HACK: append the filename at the end, hope that it will do
+                command << wxT(" %s");
+            }
         }
     }
         }
     }
+    //else: no such file type or no value, will return empty string
 
 
-    // no such file type or no value
-    return FALSE;
+    return command;
 }
 
 }
 
-// @@ this function is half implemented
+bool
+wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
+                               const wxFileType::MessageParameters& params)
+                               const
+{
+    wxString cmd;
+    if ( m_info ) {
+        cmd = m_info->GetOpenCommand();
+    }
+    else {
+        cmd = GetCommand(wxT("open"));
+    }
+
+    *openCmd = wxFileType::ExpandCommand(cmd, params);
+
+    return !openCmd->IsEmpty();
+}
+
+bool
+wxFileTypeImpl::GetPrintCommand(wxString *printCmd,
+                                const wxFileType::MessageParameters& params)
+                                const
+{
+    wxString cmd;
+    if ( m_info ) {
+        cmd = m_info->GetPrintCommand();
+    }
+    else {
+        cmd = GetCommand(wxT("print"));
+    }
+
+    *printCmd = wxFileType::ExpandCommand(cmd, params);
+
+    return !printCmd->IsEmpty();
+}
+
+// TODO this function is half implemented
 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
 {
 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
 {
-    if ( m_ext.IsEmpty() ) {
+    if ( m_info ) {
+        extensions = m_info->GetExtensions();
+
+        return TRUE;
+    }
+    else if ( m_ext.IsEmpty() ) {
         // the only way to get the list of extensions from the file type is to
         // scan through all extensions in the registry - too slow...
         return FALSE;
         // the only way to get the list of extensions from the file type is to
         // scan through all extensions in the registry - too slow...
         return FALSE;
@@ -533,10 +838,17 @@ bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
 
 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
 {
 
 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
 {
+    if ( m_info ) {
+        // we already have it
+        *mimeType = m_info->GetMimeType();
+
+        return TRUE;
+    }
+
     // suppress possible error messages
     wxLogNull nolog;
     // suppress possible error messages
     wxLogNull nolog;
-    wxRegKey key(wxRegKey::HKCR, m_strFileType);
-    if ( key.Open() && key.QueryValue("Content Type", *mimeType) ) {
+    wxRegKey key(wxRegKey::HKCR, wxT(".") + m_ext);
+    if ( key.Open() && key.QueryValue(wxT("Content Type"), *mimeType) ) {
         return TRUE;
     }
     else {
         return TRUE;
     }
     else {
@@ -546,8 +858,14 @@ bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
 
 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
 {
 
 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
 {
+#if wxUSE_GUI
+    if ( m_info ) {
+        // we don't have icons in the fallback resources
+        return FALSE;
+    }
+
     wxString strIconKey;
     wxString strIconKey;
-    strIconKey << m_strFileType << "\\DefaultIcon";
+    strIconKey << m_strFileType << wxT("\\DefaultIcon");
 
     // suppress possible error messages
     wxLogNull nolog;
 
     // suppress possible error messages
     wxLogNull nolog;
@@ -556,28 +874,28 @@ bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
     if ( key.Open() ) {
         wxString strIcon;
         // it's the default value of the key
     if ( key.Open() ) {
         wxString strIcon;
         // it's the default value of the key
-        if ( key.QueryValue("", strIcon) ) {
+        if ( key.QueryValue(wxT(""), strIcon) ) {
             // the format is the following: <full path to file>, <icon index>
             // NB: icon index may be negative as well as positive and the full
             //     path may contain the environment variables inside '%'
             // the format is the following: <full path to file>, <icon index>
             // NB: icon index may be negative as well as positive and the full
             //     path may contain the environment variables inside '%'
-            wxString strFullPath = strIcon.BeforeLast(','),
-            strIndex = strIcon.AfterLast(',');
+            wxString strFullPath = strIcon.BeforeLast(wxT(',')),
+            strIndex = strIcon.AfterLast(wxT(','));
 
             // index may be omitted, in which case BeforeLast(',') is empty and
             // AfterLast(',') is the whole string
             if ( strFullPath.IsEmpty() ) {
                 strFullPath = strIndex;
 
             // index may be omitted, in which case BeforeLast(',') is empty and
             // AfterLast(',') is the whole string
             if ( strFullPath.IsEmpty() ) {
                 strFullPath = strIndex;
-                strIndex = "0";
+                strIndex = wxT("0");
             }
 
             wxString strExpPath = wxExpandEnvVars(strFullPath);
             }
 
             wxString strExpPath = wxExpandEnvVars(strFullPath);
-            int nIndex = atoi(strIndex);
+            int nIndex = wxAtoi(strIndex);
 
             HICON hIcon = ExtractIcon(GetModuleHandle(NULL), strExpPath, nIndex);
             switch ( (int)hIcon ) {
                 case 0: // means no icons were found
                 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
 
             HICON hIcon = ExtractIcon(GetModuleHandle(NULL), strExpPath, nIndex);
             switch ( (int)hIcon ) {
                 case 0: // means no icons were found
                 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
-                    wxLogDebug("incorrect registry entry '%s': no such icon.",
+                    wxLogDebug(wxT("incorrect registry entry '%s': no such icon."),
                                key.GetName().c_str());
                     break;
 
                                key.GetName().c_str());
                     break;
 
@@ -589,18 +907,27 @@ bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
     }
 
     // no such file type or no value or incorrect icon entry
     }
 
     // no such file type or no value or incorrect icon entry
+#endif // wxUSE_GUI
+
     return FALSE;
 }
 
 bool wxFileTypeImpl::GetDescription(wxString *desc) const
 {
     return FALSE;
 }
 
 bool wxFileTypeImpl::GetDescription(wxString *desc) const
 {
+    if ( m_info ) {
+        // we already have it
+        *desc = m_info->GetDescription();
+
+        return TRUE;
+    }
+
     // suppress possible error messages
     wxLogNull nolog;
     wxRegKey key(wxRegKey::HKCR, m_strFileType);
 
     if ( key.Open() ) {
         // it's the default value of the key
     // suppress possible error messages
     wxLogNull nolog;
     wxRegKey key(wxRegKey::HKCR, m_strFileType);
 
     if ( key.Open() ) {
         // it's the default value of the key
-        if ( key.QueryValue("", *desc) ) {
+        if ( key.QueryValue(wxT(""), *desc) ) {
             return TRUE;
         }
     }
             return TRUE;
         }
     }
@@ -614,41 +941,67 @@ wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
 {
     // add the leading point if necessary
     wxString str;
 {
     // add the leading point if necessary
     wxString str;
-    if ( ext[0u] != '.' ) {
-        str = '.';
+    if ( ext[0u] != wxT('.') ) {
+        str = wxT('.');
     }
     str << ext;
 
     // suppress possible error messages
     wxLogNull nolog;
 
     }
     str << ext;
 
     // suppress possible error messages
     wxLogNull nolog;
 
+    bool knownExtension = FALSE;
+
     wxString strFileType;
     wxRegKey key(wxRegKey::HKCR, str);
     if ( key.Open() ) {
         // it's the default value of the key
     wxString strFileType;
     wxRegKey key(wxRegKey::HKCR, str);
     if ( key.Open() ) {
         // it's the default value of the key
-        if ( key.QueryValue("", strFileType) ) {
+        if ( key.QueryValue(wxT(""), strFileType) ) {
             // create the new wxFileType object
             wxFileType *fileType = new wxFileType;
             // create the new wxFileType object
             wxFileType *fileType = new wxFileType;
-            fileType->m_impl->SetFileType(strFileType);
-            fileType->m_impl->SetExt(ext);
+            fileType->m_impl->Init(strFileType, ext);
 
             return fileType;
         }
 
             return fileType;
         }
+        else {
+            // this extension doesn't have a filetype, but it's known to the
+            // system and may be has some other useful keys (open command or
+            // content-type), so still return a file type object for it
+            knownExtension = TRUE;
+        }
     }
 
     }
 
-    // unknown extension
-    return NULL;
+    // check the fallbacks
+    // TODO linear search is potentially slow, perhaps we should use a sorted
+    //      array?
+    size_t count = m_fallbacks.GetCount();
+    for ( size_t n = 0; n < count; n++ ) {
+        if ( m_fallbacks[n].GetExtensions().Index(ext) != wxNOT_FOUND ) {
+            wxFileType *fileType = new wxFileType;
+            fileType->m_impl->Init(m_fallbacks[n]);
+
+            return fileType;
+        }
+    }
+
+    if ( knownExtension )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->Init(wxEmptyString, ext);
+
+        return fileType;
+    }
+    else
+    {
+        // unknown extension
+        return NULL;
+    }
 }
 
 // MIME type -> extension -> file type
 wxFileType *
 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
 {
 }
 
 // MIME type -> extension -> file type
 wxFileType *
 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
 {
-    // @@@ I don't know of any official documentation which mentions this
-    //     location, but as a matter of fact IE uses it, so why not we?
-    static const char *szMimeDbase = "MIME\\Database\\Content Type\\";
-
-    wxString strKey = szMimeDbase;
+    wxString strKey = MIME_DATABASE_KEY;
     strKey << mimeType;
 
     // suppress possible error messages
     strKey << mimeType;
 
     // suppress possible error messages
@@ -657,49 +1010,600 @@ wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
     wxString ext;
     wxRegKey key(wxRegKey::HKCR, strKey);
     if ( key.Open() ) {
     wxString ext;
     wxRegKey key(wxRegKey::HKCR, strKey);
     if ( key.Open() ) {
-        if ( key.QueryValue("Extension", ext) ) {
+        if ( key.QueryValue(wxT("Extension"), ext) ) {
             return GetFileTypeFromExtension(ext);
         }
     }
 
             return GetFileTypeFromExtension(ext);
         }
     }
 
+    // check the fallbacks
+    // TODO linear search is potentially slow, perhaps we should use a sorted
+    //      array?
+    size_t count = m_fallbacks.GetCount();
+    for ( size_t n = 0; n < count; n++ ) {
+        if ( wxMimeTypesManager::IsOfType(mimeType,
+                                          m_fallbacks[n].GetMimeType()) ) {
+            wxFileType *fileType = new wxFileType;
+            fileType->m_impl->Init(m_fallbacks[n]);
+
+            return fileType;
+        }
+    }
+
     // unknown MIME type
     return NULL;
 }
 
     // unknown MIME type
     return NULL;
 }
 
-#else  // Unix
-
-MailCapEntry *
-wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters& params) const
+size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
 {
 {
-    wxString command;
-    MailCapEntry *entry = m_manager->m_aEntries[m_index];
-    while ( entry != NULL ) {
-        // notice that an empty command would always succeed (@@ is it ok?)
-        command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
+    // enumerate all keys under MIME_DATABASE_KEY
+    wxRegKey key(wxRegKey::HKCR, MIME_DATABASE_KEY);
 
 
-        if ( command.IsEmpty() || (system(command) == 0) ) {
-            // ok, passed
-            wxLogTrace("Test '%s' for mime type '%s' succeeded.",
-                       command.c_str(), params.GetMimeType().c_str());
-            break;
-        }
-        else {
-            wxLogTrace("Test '%s' for mime type '%s' failed.",
-                       command.c_str(), params.GetMimeType().c_str());
-        }
+    wxString type;
+    long cookie;
+    bool cont = key.GetFirstKey(type, cookie);
+    while ( cont )
+    {
+        mimetypes.Add(type);
 
 
-        entry = entry->GetNext();
+        cont = key.GetNextKey(type, cookie);
     }
 
     }
 
-    return entry;
+    return mimetypes.GetCount();
 }
 
 }
 
-bool
-wxFileTypeImpl::GetExpandedCommand(wxString *expandedCmd,
-                                   const wxFileType::MessageParameters& params,
-                                   bool open) const
+#elif defined ( __WXMAC__ )
+
+bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const
 {
 {
-    MailCapEntry *entry = GetEntry(params);
+    return FALSE;
+}
+
+// @@ this function is half implemented
+bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
+{
+    return FALSE;
+}
+
+bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
+{
+    if ( m_strFileType.Length() > 0 )
+    {
+        *mimeType = m_strFileType ;
+        return TRUE ;
+    }
+    else
+    return FALSE;
+}
+
+bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
+{
+    // no such file type or no value or incorrect icon entry
+    return FALSE;
+}
+
+bool wxFileTypeImpl::GetDescription(wxString *desc) const
+{
+    return FALSE;
+}
+
+// extension -> file type
+wxFileType *
+wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e)
+{
+    wxString ext = e ;
+    ext = ext.Lower() ;
+    if ( ext == "txt" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("text/text");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+    else if ( ext == "htm" || ext == "html" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("text/html");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+    else if ( ext == "gif" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("image/gif");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+    else if ( ext == "png" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("image/png");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+    else if ( ext == "jpg" || ext == "jpeg" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("image/jpeg");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+    else if ( ext == "bmp" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("image/bmp");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+    else if ( ext == "tif" || ext == "tiff" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("image/tiff");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+    else if ( ext == "xpm" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("image/xpm");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+    else if ( ext == "xbm" )
+    {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->SetFileType("image/xbm");
+        fileType->m_impl->SetExt(ext);
+        return fileType;
+    }
+
+    // unknown extension
+    return NULL;
+}
+
+// MIME type -> extension -> file type
+wxFileType *
+wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
+{
+    return NULL;
+}
+
+size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
+{
+    wxFAIL_MSG( _T("TODO") ); // VZ: don't know anything about this for Mac
+
+    return 0;
+}
+
+#else  // Unix
+
+// ============================================================================
+// Unix implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// various statics
+// ----------------------------------------------------------------------------
+
+static wxGNOMEIconHandler gs_iconHandlerGNOME;
+static wxKDEIconHandler gs_iconHandlerKDE;
+
+bool wxGNOMEIconHandler::m_inited = FALSE;
+wxSortedArrayString wxGNOMEIconHandler::ms_mimetypes;
+wxArrayString       wxGNOMEIconHandler::ms_icons;
+
+bool wxKDEIconHandler::m_inited = FALSE;
+wxSortedArrayString wxKDEIconHandler::ms_mimetypes;
+wxArrayString       wxKDEIconHandler::ms_icons;
+
+ArrayIconHandlers wxMimeTypesManagerImpl::ms_iconHandlers;
+
+// ----------------------------------------------------------------------------
+// wxGNOMEIconHandler
+// ----------------------------------------------------------------------------
+
+// GNOME stores the info we're interested in in several locations:
+//  1. xxx.keys files under /usr/share/mime-info
+//  2. xxx.keys files under ~/.gnome/mime-info
+//
+// The format of xxx.keys file is the following:
+//
+// mimetype/subtype:
+//      field=value
+//
+// with blank lines separating the entries and indented lines starting with
+// TABs. We're interested in the field icon-filename whose value is the path
+// containing the icon.
+
+void wxGNOMEIconHandler::LoadIconsFromKeyFile(const wxString& filename)
+{
+    wxTextFile textfile(filename);
+    if ( !textfile.Open() )
+        return;
+
+    // values for the entry being parsed
+    wxString curMimeType, curIconFile;
+
+    const wxChar *pc;
+    size_t nLineCount = textfile.GetLineCount();
+    for ( size_t nLine = 0; ; nLine++ )
+    {
+        if ( nLine < nLineCount )
+        {
+            pc = textfile[nLine].c_str();
+            if ( *pc == _T('#') )
+            {
+                // skip comments
+                continue;
+            }
+        }
+        else
+        {
+            // so that we will fall into the "if" below
+            pc = NULL;
+        }
+
+        if ( !pc || !*pc )
+        {
+            // end of the entry
+            if ( !!curMimeType && !!curIconFile )
+            {
+                // do we already know this mimetype?
+                int i = ms_mimetypes.Index(curMimeType);
+                if ( i == wxNOT_FOUND )
+                {
+                    // add a new entry
+                    size_t n = ms_mimetypes.Add(curMimeType);
+                    ms_icons.Insert(curIconFile, n);
+                }
+                else
+                {
+                    // replace the existing one (this means that the directories
+                    // should be searched in order of increased priority!)
+                    ms_icons[(size_t)i] = curIconFile;
+                }
+            }
+
+            if ( !pc )
+            {
+                // the end - this can only happen if nLine == nLineCount
+                break;
+            }
+
+            curIconFile.Empty();
+
+            continue;
+        }
+
+        // what do we have here?
+        if ( *pc == _T('\t') )
+        {
+            // this is a field=value ling
+            pc++; // skip leading TAB
+
+            static const int lenField = 13; // strlen("icon-filename")
+            if ( wxStrncmp(pc, _T("icon-filename"), lenField) == 0 )
+            {
+                // skip '=' which follows and take everything left until the end
+                // of line
+                curIconFile = pc + lenField + 1;
+            }
+            //else: some other field, we don't care
+        }
+        else
+        {
+            // this is the start of the new section
+            curMimeType.Empty();
+
+            while ( *pc != _T(':') && *pc != _T('\0') )
+            {
+                curMimeType += *pc++;
+            }
+
+            if ( !*pc )
+            {
+                // we reached the end of line without finding the colon,
+                // something is wrong - ignore this line completely
+                wxLogDebug(_T("Unreckognized line %d in file '%s' ignored"),
+                           nLine + 1, filename.c_str());
+
+                break;
+            }
+        }
+    }
+}
+
+void wxGNOMEIconHandler::LoadKeyFilesFromDir(const wxString& dirbase)
+{
+    wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
+                  _T("base directory shouldn't end with a slash") );
+
+    wxString dirname = dirbase;
+    dirname << _T("/mime-info");
+
+    if ( !wxDir::Exists(dirname) )
+        return;
+
+    wxDir dir(dirname);
+    if ( !dir.IsOpened() )
+        return;
+
+    // we will concatenate it with filename to get the full path below
+    dirname += _T('/');
+
+    wxString filename;
+    bool cont = dir.GetFirst(&filename, _T("*.keys"), wxDIR_FILES);
+    while ( cont )
+    {
+        LoadIconsFromKeyFile(dirname + filename);
+
+        cont = dir.GetNext(&filename);
+    }
+}
+
+void wxGNOMEIconHandler::Init()
+{
+    wxArrayString dirs;
+    dirs.Add(_T("/usr/share"));
+
+    wxString gnomedir;
+    wxGetHomeDir( &gnomedir );
+    gnomedir += _T("/.gnome");
+    dirs.Add( gnomedir );
+
+    size_t nDirs = dirs.GetCount();
+    for ( size_t nDir = 0; nDir < nDirs; nDir++ )
+    {
+        LoadKeyFilesFromDir(dirs[nDir]);
+    }
+
+    m_inited = TRUE;
+}
+
+bool wxGNOMEIconHandler::GetIcon(const wxString& mimetype, wxIcon *icon)
+{
+    if ( !m_inited )
+    {
+        Init();
+    }
+
+    int index = ms_mimetypes.Index(mimetype);
+    if ( index == wxNOT_FOUND )
+        return FALSE;
+
+    wxString iconname = ms_icons[(size_t)index];
+
+#if wxUSE_GUI
+    *icon = wxIcon(iconname);
+#else
+    // helpful for testing in console mode
+    wxLogDebug(_T("Found GNOME icon for '%s': '%s'\n"),
+               mimetype.c_str(), iconname.c_str());
+#endif
+
+    return TRUE;
+}
+
+// ----------------------------------------------------------------------------
+// wxKDEIconHandler
+// ----------------------------------------------------------------------------
+
+// KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype
+// may be found in either of the following locations
+//
+//  1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
+//  2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
+//
+// The format of a .kdelnk file is almost the same as the one used by
+// wxFileConfig, i.e. there are groups, comments and entries. The icon is the
+// value for the entry "Type"
+
+void wxKDEIconHandler::LoadLinksForMimeSubtype(const wxString& dirbase,
+                                               const wxString& subdir,
+                                               const wxString& filename)
+{
+    wxFFile file(dirbase + filename);
+    if ( !file.IsOpened() )
+        return;
+
+    // these files are small, slurp the entire file at once
+    wxString text;
+    if ( !file.ReadAll(&text) )
+        return;
+
+    int pos = text.Find(_T("Icon="));
+    if ( pos == wxNOT_FOUND )
+    {
+        // no icon info
+        return;
+    }
+
+    wxString icon;
+
+    const wxChar *pc = text.c_str() + pos + 5;  // 5 == strlen("Icon=")
+    while ( *pc && *pc != _T('\n') )
+    {
+        icon += *pc++;
+    }
+
+    if ( !!icon )
+    {
+        // don't check that the file actually exists - would be too slow
+        icon.Prepend(_T("/usr/share/icons/"));
+
+        // construct mimetype from the directory name and the basename of the
+        // file (it always has .kdelnk extension)
+        wxString mimetype;
+        mimetype << subdir << _T('/') << filename.BeforeLast(_T('.'));
+
+        // do we already have this MIME type?
+        int i = ms_mimetypes.Index(mimetype);
+        if ( i == wxNOT_FOUND )
+        {
+            // add it
+            size_t n = ms_mimetypes.Add(mimetype);
+            ms_icons.Insert(icon, n);
+        }
+        else
+        {
+            // replace the old value
+            ms_icons[(size_t)i] = icon;
+        }
+    }
+}
+
+void wxKDEIconHandler::LoadLinksForMimeType(const wxString& dirbase,
+                                            const wxString& subdir)
+{
+    wxString dirname = dirbase;
+    dirname += subdir;
+    wxDir dir(dirname);
+    if ( !dir.IsOpened() )
+        return;
+
+    dirname += _T('/');
+
+    wxString filename;
+    bool cont = dir.GetFirst(&filename, _T("*.kdelnk"), wxDIR_FILES);
+    while ( cont )
+    {
+        LoadLinksForMimeSubtype(dirname, subdir, filename);
+
+        cont = dir.GetNext(&filename);
+    }
+}
+
+void wxKDEIconHandler::LoadLinkFilesFromDir(const wxString& dirbase)
+{
+    wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
+                  _T("base directory shouldn't end with a slash") );
+
+    wxString dirname = dirbase;
+    dirname << _T("/mimelnk");
+
+    if ( !wxDir::Exists(dirname) )
+        return;
+
+    wxDir dir(dirname);
+    if ( !dir.IsOpened() )
+        return;
+
+    // we will concatenate it with dir name to get the full path below
+    dirname += _T('/');
+
+    wxString subdir;
+    bool cont = dir.GetFirst(&subdir, wxEmptyString, wxDIR_DIRS);
+    while ( cont )
+    {
+        LoadLinksForMimeType(dirname, subdir);
+
+        cont = dir.GetNext(&subdir);
+    }
+}
+
+void wxKDEIconHandler::Init()
+{
+    wxArrayString dirs;
+
+    // the variable KDEDIR is set when KDE is running
+    const char *kdedir = getenv("KDEDIR");
+    if ( kdedir )
+    {
+        dirs.Add(wxString(kdedir) + _T("/share"));
+    }
+    else
+    {
+        // try to guess KDEDIR
+        dirs.Add(_T("/usr/share"));
+        dirs.Add(_T("/opt/kde/share"));
+    }
+
+    dirs.Add(wxGetHomeDir() + _T("/.kde/share"));
+
+    size_t nDirs = dirs.GetCount();
+    for ( size_t nDir = 0; nDir < nDirs; nDir++ )
+    {
+        LoadLinkFilesFromDir(dirs[nDir]);
+    }
+
+    m_inited = TRUE;
+}
+
+bool wxKDEIconHandler::GetIcon(const wxString& mimetype, wxIcon *icon)
+{
+    if ( !m_inited )
+    {
+        Init();
+    }
+
+    int index = ms_mimetypes.Index(mimetype);
+    if ( index == wxNOT_FOUND )
+        return FALSE;
+
+    wxString iconname = ms_icons[(size_t)index];
+
+#if wxUSE_GUI
+    *icon = wxIcon(iconname);
+#else
+    // helpful for testing in console mode
+    wxLogDebug(_T("Found KDE icon for '%s': '%s'\n"),
+               mimetype.c_str(), iconname.c_str());
+#endif
+
+    return TRUE;
+}
+
+// ----------------------------------------------------------------------------
+// wxFileTypeImpl (Unix)
+// ----------------------------------------------------------------------------
+
+MailCapEntry *
+wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters& params) const
+{
+    wxString command;
+    MailCapEntry *entry = m_manager->m_aEntries[m_index];
+    while ( entry != NULL ) {
+        // notice that an empty command would always succeed (it's ok)
+        command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
+
+        if ( command.IsEmpty() || (wxSystem(command) == 0) ) {
+            // ok, passed
+            wxLogTrace(wxT("Test '%s' for mime type '%s' succeeded."),
+                       command.c_str(), params.GetMimeType().c_str());
+            break;
+        }
+        else {
+            wxLogTrace(wxT("Test '%s' for mime type '%s' failed."),
+                       command.c_str(), params.GetMimeType().c_str());
+        }
+
+        entry = entry->GetNext();
+    }
+
+    return entry;
+}
+
+bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
+{
+    wxString mimetype;
+    (void)GetMimeType(&mimetype);
+
+    ArrayIconHandlers& handlers = m_manager->GetIconHandlers();
+    size_t count = handlers.GetCount();
+    for ( size_t n = 0; n < count; n++ )
+    {
+        if ( handlers[n]->GetIcon(mimetype, icon) )
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+bool
+wxFileTypeImpl::GetExpandedCommand(wxString *expandedCmd,
+                                   const wxFileType::MessageParameters& params,
+                                   bool open) const
+{
+    MailCapEntry *entry = GetEntry(params);
     if ( entry == NULL ) {
         // all tests failed...
         return FALSE;
     if ( entry == NULL ) {
         // all tests failed...
         return FALSE;
@@ -722,8 +1626,8 @@ bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
 
     // one extension in the space or comma delimitid list
     wxString strExt;
 
     // one extension in the space or comma delimitid list
     wxString strExt;
-    for ( const char *p = strExtensions; ; p++ ) {
-        if ( *p == ' ' || *p == ',' || *p == '\0' ) {
+    for ( const wxChar *p = strExtensions; ; p++ ) {
+        if ( *p == wxT(' ') || *p == wxT(',') || *p == wxT('\0') ) {
             if ( !strExt.IsEmpty() ) {
                 extensions.Add(strExt);
                 strExt.Empty();
             if ( !strExt.IsEmpty() ) {
                 extensions.Add(strExt);
                 strExt.Empty();
@@ -731,13 +1635,13 @@ bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
             //else: repeated spaces (shouldn't happen, but it's not that
             //      important if it does happen)
 
             //else: repeated spaces (shouldn't happen, but it's not that
             //      important if it does happen)
 
-            if ( *p == '\0' )
+            if ( *p == wxT('\0') )
                 break;
         }
                 break;
         }
-        else if ( *p == '.' ) {
+        else if ( *p == wxT('.') ) {
             // remove the dot from extension (but only if it's the first char)
             if ( !strExt.IsEmpty() ) {
             // remove the dot from extension (but only if it's the first char)
             if ( !strExt.IsEmpty() ) {
-                strExt += '.';
+                strExt += wxT('.');
             }
             //else: no, don't append it
         }
             }
             //else: no, don't append it
         }
@@ -749,45 +1653,61 @@ bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
     return TRUE;
 }
 
     return TRUE;
 }
 
+// ----------------------------------------------------------------------------
+// wxMimeTypesManagerImpl (Unix)
+// ----------------------------------------------------------------------------
+
+/* static */
+ArrayIconHandlers& wxMimeTypesManagerImpl::GetIconHandlers()
+{
+    if ( ms_iconHandlers.GetCount() == 0 )
+    {
+        ms_iconHandlers.Add(&gs_iconHandlerGNOME);
+        ms_iconHandlers.Add(&gs_iconHandlerKDE);
+    }
+
+    return ms_iconHandlers;
+}
+
 // read system and user mailcaps (TODO implement mime.types support)
 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
 {
     // directories where we look for mailcap and mime.types by default
     // (taken from metamail(1) sources)
 // read system and user mailcaps (TODO implement mime.types support)
 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
 {
     // directories where we look for mailcap and mime.types by default
     // (taken from metamail(1) sources)
-    static const char *aStandardLocations[] =
+    static const wxChar *aStandardLocations[] =
     {
     {
-        "/etc",
-        "/usr/etc",
-        "/usr/local/etc",
-        "/etc/mail",
-        "/usr/public/lib"
+        wxT("/etc"),
+        wxT("/usr/etc"),
+        wxT("/usr/local/etc"),
+        wxT("/etc/mail"),
+        wxT("/usr/public/lib")
     };
 
     // first read the system wide file(s)
     for ( size_t n = 0; n < WXSIZEOF(aStandardLocations); n++ ) {
         wxString dir = aStandardLocations[n];
 
     };
 
     // first read the system wide file(s)
     for ( size_t n = 0; n < WXSIZEOF(aStandardLocations); n++ ) {
         wxString dir = aStandardLocations[n];
 
-        wxString file = dir + "/mailcap";
+        wxString file = dir + wxT("/mailcap");
         if ( wxFile::Exists(file) ) {
             ReadMailcap(file);
         }
 
         if ( wxFile::Exists(file) ) {
             ReadMailcap(file);
         }
 
-        file = dir + "/mime.types";
+        file = dir + wxT("/mime.types");
         if ( wxFile::Exists(file) ) {
             ReadMimeTypes(file);
         }
     }
 
         if ( wxFile::Exists(file) ) {
             ReadMimeTypes(file);
         }
     }
 
-    wxString strHome = getenv("HOME");
+    wxString strHome = wxGetenv(wxT("HOME"));
 
     // and now the users mailcap
 
     // and now the users mailcap
-    wxString strUserMailcap = strHome + "/.mailcap";
+    wxString strUserMailcap = strHome + wxT("/.mailcap");
     if ( wxFile::Exists(strUserMailcap) ) {
         ReadMailcap(strUserMailcap);
     }
 
     // read the users mime.types
     if ( wxFile::Exists(strUserMailcap) ) {
         ReadMailcap(strUserMailcap);
     }
 
     // read the users mime.types
-    wxString strUserMimeTypes = strHome + "/.mime.types";
+    wxString strUserMimeTypes = strHome + wxT("/.mime.types");
     if ( wxFile::Exists(strUserMimeTypes) ) {
         ReadMimeTypes(strUserMimeTypes);
     }
     if ( wxFile::Exists(strUserMimeTypes) ) {
         ReadMimeTypes(strUserMimeTypes);
     }
@@ -800,15 +1720,15 @@ wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
     for ( size_t n = 0; n < count; n++ ) {
         wxString extensions = m_aExtensions[n];
         while ( !extensions.IsEmpty() ) {
     for ( size_t n = 0; n < count; n++ ) {
         wxString extensions = m_aExtensions[n];
         while ( !extensions.IsEmpty() ) {
-            wxString field = extensions.BeforeFirst(' ');
-            extensions = extensions.AfterFirst(' ');
+            wxString field = extensions.BeforeFirst(wxT(' '));
+            extensions = extensions.AfterFirst(wxT(' '));
 
             // consider extensions as not being case-sensitive
 
             // consider extensions as not being case-sensitive
-            if ( field.IsSameAs(ext, FALSE /* no case */) ) { 
+            if ( field.IsSameAs(ext, FALSE /* no case */) ) {
                 // found
                 wxFileType *fileType = new wxFileType;
                 fileType->m_impl->Init(this, n);
                 // found
                 wxFileType *fileType = new wxFileType;
                 fileType->m_impl->Init(this, n);
-                
+
                 return fileType;
             }
         }
                 return fileType;
             }
         }
@@ -831,12 +1751,12 @@ wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
         // then try to find "text/*" as match for "text/plain" (for example)
         // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
         //     the whole string - ok.
         // then try to find "text/*" as match for "text/plain" (for example)
         // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
         //     the whole string - ok.
-        wxString strCategory = mimetype.BeforeFirst('/');
+        wxString strCategory = mimetype.BeforeFirst(wxT('/'));
 
         size_t nCount = m_aTypes.Count();
         for ( size_t n = 0; n < nCount; n++ ) {
 
         size_t nCount = m_aTypes.Count();
         for ( size_t n = 0; n < nCount; n++ ) {
-            if ( (m_aTypes[n].BeforeFirst('/') == strCategory ) &&
-                 m_aTypes[n].AfterFirst('/') == "*" ) {
+            if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
+                 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") ) {
                     index = n;
                     break;
             }
                     index = n;
                     break;
             }
@@ -855,43 +1775,125 @@ wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
     }
 }
 
     }
 }
 
-void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
+void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
+{
+    wxString extensions;
+    const wxArrayString& exts = filetype.GetExtensions();
+    size_t nExts = exts.GetCount();
+    for ( size_t nExt = 0; nExt < nExts; nExt++ ) {
+        if ( nExt > 0 ) {
+            extensions += wxT(' ');
+        }
+        extensions += exts[nExt];
+    }
+
+    AddMimeTypeInfo(filetype.GetMimeType(),
+                    extensions,
+                    filetype.GetDescription());
+
+    AddMailcapInfo(filetype.GetMimeType(),
+                   filetype.GetOpenCommand(),
+                   filetype.GetPrintCommand(),
+                   wxT(""),
+                   filetype.GetDescription());
+}
+
+void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
+                                             const wxString& strExtensions,
+                                             const wxString& strDesc)
+{
+    int index = m_aTypes.Index(strMimeType);
+    if ( index == wxNOT_FOUND ) {
+        // add a new entry
+        m_aTypes.Add(strMimeType);
+        m_aEntries.Add(NULL);
+        m_aExtensions.Add(strExtensions);
+        m_aDescriptions.Add(strDesc);
+    }
+    else {
+        // modify an existing one
+        if ( !strDesc.IsEmpty() ) {
+            m_aDescriptions[index] = strDesc;   // replace old value
+        }
+        m_aExtensions[index] += ' ' + strExtensions;
+    }
+}
+
+void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString& strType,
+                                            const wxString& strOpenCmd,
+                                            const wxString& strPrintCmd,
+                                            const wxString& strTest,
+                                            const wxString& strDesc)
+{
+    MailCapEntry *entry = new MailCapEntry(strOpenCmd, strPrintCmd, strTest);
+
+    int nIndex = m_aTypes.Index(strType);
+    if ( nIndex == wxNOT_FOUND ) {
+        // new file type
+        m_aTypes.Add(strType);
+
+        m_aEntries.Add(entry);
+        m_aExtensions.Add(wxT(""));
+        m_aDescriptions.Add(strDesc);
+    }
+    else {
+        // always append the entry in the tail of the list - info added with
+        // this function can only come from AddFallbacks()
+        MailCapEntry *entryOld = m_aEntries[nIndex];
+        if ( entryOld )
+            entry->Append(entryOld);
+        else
+            m_aEntries[nIndex] = entry;
+    }
+}
+
+bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
 {
 {
-    wxLogTrace("--- Parsing mime.types file '%s' ---", strFileName.c_str());
+    wxLogTrace(wxT("--- Parsing mime.types file '%s' ---"), strFileName.c_str());
 
     wxTextFile file(strFileName);
     if ( !file.Open() )
 
     wxTextFile file(strFileName);
     if ( !file.Open() )
-        return;
+        return FALSE;
 
     // the information we extract
     wxString strMimeType, strDesc, strExtensions;
 
     size_t nLineCount = file.GetLineCount();
 
     // the information we extract
     wxString strMimeType, strDesc, strExtensions;
 
     size_t nLineCount = file.GetLineCount();
+    const wxChar *pc = NULL;
     for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
     for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
-        // now we're at the start of the line
-        const char *pc = file[nLine].c_str();
+        if ( pc == NULL ) {
+            // now we're at the start of the line
+            pc = file[nLine].c_str();
+        }
+        else {
+            // we didn't finish with the previous line yet
+            nLine--;
+        }
 
         // skip whitespace
 
         // skip whitespace
-        while ( isspace(*pc) )
+        while ( wxIsspace(*pc) )
             pc++;
 
             pc++;
 
-        // comment?
-        if ( *pc == '#' )
+        // comment or blank line?
+        if ( *pc == wxT('#') || !*pc ) {
+            // skip the whole line
+            pc = NULL;
             continue;
             continue;
+        }
 
         // detect file format
 
         // detect file format
-        const char *pEqualSign = strchr(pc, '=');
+        const wxChar *pEqualSign = wxStrchr(pc, wxT('='));
         if ( pEqualSign == NULL ) {
             // brief format
             // ------------
 
             // first field is mime type
         if ( pEqualSign == NULL ) {
             // brief format
             // ------------
 
             // first field is mime type
-            for ( strMimeType.Empty(); !isspace(*pc) && *pc != '\0'; pc++ ) {
+            for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != wxT('\0'); pc++ ) {
                 strMimeType += *pc;
             }
 
             // skip whitespace
                 strMimeType += *pc;
             }
 
             // skip whitespace
-            while ( isspace(*pc) )
+            while ( wxIsspace(*pc) )
                 pc++;
 
             // take all the rest of the string
                 pc++;
 
             // take all the rest of the string
@@ -908,13 +1910,13 @@ void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
             wxString strLHS(pc, pEqualSign - pc);
 
             // eat whitespace
             wxString strLHS(pc, pEqualSign - pc);
 
             // eat whitespace
-            for ( pc = pEqualSign + 1; isspace(*pc); pc++ )
+            for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
                 ;
 
                 ;
 
-            const char *pEnd;
-            if ( *pc == '"' ) {
+            const wxChar *pEnd;
+            if ( *pc == wxT('"') ) {
                 // the string is quoted and ends at the matching quote
                 // the string is quoted and ends at the matching quote
-                pEnd = strchr(++pc, '"');
+                pEnd = wxStrchr(++pc, wxT('"'));
                 if ( pEnd == NULL ) {
                     wxLogWarning(_("Mime.types file %s, line %d: unterminated "
                                    "quoted string."),
                 if ( pEnd == NULL ) {
                     wxLogWarning(_("Mime.types file %s, line %d: unterminated "
                                    "quoted string."),
@@ -922,41 +1924,39 @@ void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
                 }
             }
             else {
                 }
             }
             else {
-                // unquoted stringends at the first space
-                for ( pEnd = pc; !isspace(*pEnd); pEnd++ )
+                // unquoted string ends at the first space
+                for ( pEnd = pc; !wxIsspace(*pEnd); pEnd++ )
                     ;
             }
 
             // now we have the RHS (field value)
             wxString strRHS(pc, pEnd - pc);
 
                     ;
             }
 
             // now we have the RHS (field value)
             wxString strRHS(pc, pEnd - pc);
 
-            // check that it's more or less what we're waiting for, i.e. that
-            // only '\' is left on the line
-            if ( *pEnd == '"' ) {
+            // check what follows this entry
+            if ( *pEnd == wxT('"') ) {
                 // skip this quote
                 pEnd++;
             }
 
                 // skip this quote
                 pEnd++;
             }
 
-            for ( pc = pEnd; isspace(*pc); pc++ )
+            for ( pc = pEnd; wxIsspace(*pc); pc++ )
                 ;
 
                 ;
 
-            // only '\\' may be left on the line normally
-            bool entryEnded = *pc == '\0';
-            if ( !entryEnded && ((*pc != '\\') || (*++pc != '\0')) ) {
-                wxLogWarning(_("Mime.types file %s, line %d: extra characters "
-                               "after the field value ignored."),
-                             strFileName.c_str(), nLine + 1);
+            // if there is something left, it may be either a '\\' to continue
+            // the line or the next field of the same entry
+            bool entryEnded = *pc == wxT('\0'),
+                 nextFieldOnSameLine = FALSE;
+            if ( !entryEnded ) {
+                nextFieldOnSameLine = ((*pc != wxT('\\')) || (pc[1] != wxT('\0')));
             }
             }
-            // if there is a trailing backslash entryEnded = FALSE
 
             // now see what we got
 
             // now see what we got
-            if ( strLHS == "type" ) {
+            if ( strLHS == wxT("type") ) {
                 strMimeType = strRHS;
             }
                 strMimeType = strRHS;
             }
-            else if ( strLHS == "desc" ) {
+            else if ( strLHS == wxT("desc") ) {
                 strDesc = strRHS;
             }
                 strDesc = strRHS;
             }
-            else if ( strLHS == "exts" ) {
+            else if ( strLHS == wxT("exts") ) {
                 strExtensions = strRHS;
             }
             else {
                 strExtensions = strRHS;
             }
             else {
@@ -965,57 +1965,70 @@ void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
             }
 
             if ( !entryEnded ) {
             }
 
             if ( !entryEnded ) {
-                // as we don't reset strMimeType, the next line in this entry
+                if ( !nextFieldOnSameLine )
+                    pc = NULL;
+                //else: don't reset it
+
+                // as we don't reset strMimeType, the next field in this entry
                 // will be interpreted correctly.
                 // will be interpreted correctly.
+
                 continue;
             }
         }
 
                 continue;
             }
         }
 
-        int index = m_aTypes.Index(strMimeType);
-        if ( index == wxNOT_FOUND ) {
-            // add a new entry
-            m_aTypes.Add(strMimeType);
-            m_aEntries.Add(NULL);
-            m_aExtensions.Add(strExtensions);
-            m_aDescriptions.Add(strDesc);
-        }
-        else {
-            // modify an existing one
-            if ( !strDesc.IsEmpty() ) {
-                m_aDescriptions[index] = strDesc;   // replace old value
-            }
-            m_aExtensions[index] += strExtensions;
+        // although it doesn't seem to be covered by RFCs, some programs
+        // (notably Netscape) create their entries with several comma
+        // separated extensions (RFC mention the spaces only)
+        strExtensions.Replace(wxT(","), wxT(" "));
+
+        // also deal with the leading dot
+        if ( !strExtensions.IsEmpty() && strExtensions[0u] == wxT('.') )
+        {
+            strExtensions.erase(0, 1);
         }
         }
+
+        AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
+
+        // finished with this line
+        pc = NULL;
     }
 
     // check our data integriry
     wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
               m_aTypes.Count() == m_aExtensions.Count() &&
               m_aTypes.Count() == m_aDescriptions.Count() );
     }
 
     // check our data integriry
     wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
               m_aTypes.Count() == m_aExtensions.Count() &&
               m_aTypes.Count() == m_aDescriptions.Count() );
+
+    return TRUE;
 }
 
 }
 
-void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
+bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
+                                         bool fallback)
 {
 {
-    wxLogTrace("--- Parsing mailcap file '%s' ---", strFileName.c_str());
+    wxLogTrace(wxT("--- Parsing mailcap file '%s' ---"), strFileName.c_str());
 
     wxTextFile file(strFileName);
     if ( !file.Open() )
 
     wxTextFile file(strFileName);
     if ( !file.Open() )
-        return;
+        return FALSE;
 
 
-    // see the comments near the end of function for the reason we need this
+    // see the comments near the end of function for the reason we need these
+    // variables (search for the next occurence of them)
+        // indices of MIME types (in m_aTypes) we already found in this file
     wxArrayInt aEntryIndices;
     wxArrayInt aEntryIndices;
+        // aLastIndices[n] is the index of last element in
+        // m_aEntries[aEntryIndices[n]] from this file
+    wxArrayInt aLastIndices;
 
     size_t nLineCount = file.GetLineCount();
     for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
         // now we're at the start of the line
 
     size_t nLineCount = file.GetLineCount();
     for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
         // now we're at the start of the line
-        const char *pc = file[nLine].c_str();
+        const wxChar *pc = file[nLine].c_str();
 
         // skip whitespace
 
         // skip whitespace
-        while ( isspace(*pc) )
+        while ( wxIsspace(*pc) )
             pc++;
 
         // comment or empty string?
             pc++;
 
         // comment or empty string?
-        if ( *pc == '#' || *pc == '\0' )
+        if ( *pc == wxT('#') || *pc == wxT('\0') )
             continue;
 
         // no, do parse
             continue;
 
         // no, do parse
@@ -1031,8 +2044,8 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
         } currentToken = Field_Type;
 
         // the flags and field values on the current line
         } currentToken = Field_Type;
 
         // the flags and field values on the current line
-        bool needsterminal = false,
-             copiousoutput = false;
+        bool needsterminal = FALSE,
+             copiousoutput = FALSE;
         wxString strType,
                  strOpenCmd,
                  strPrintCmd,
         wxString strType,
                  strOpenCmd,
                  strPrintCmd,
@@ -1041,10 +2054,10 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                  curField; // accumulator
         for ( bool cont = TRUE; cont; pc++ ) {
             switch ( *pc ) {
                  curField; // accumulator
         for ( bool cont = TRUE; cont; pc++ ) {
             switch ( *pc ) {
-                case '\\':
+                case wxT('\\'):
                     // interpret the next character literally (notice that
                     // backslash can be used for line continuation)
                     // interpret the next character literally (notice that
                     // backslash can be used for line continuation)
-                    if ( *++pc == '\0' ) {
+                    if ( *++pc == wxT('\0') ) {
                         // fetch the next line.
 
                         // pc currently points to nowhere, but after the next
                         // fetch the next line.
 
                         // pc currently points to nowhere, but after the next
@@ -1058,12 +2071,12 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                     }
                     break;
 
                     }
                     break;
 
-                case '\0':
+                case wxT('\0'):
                     cont = FALSE;   // end of line reached, exit the loop
 
                     // fall through
 
                     cont = FALSE;   // end of line reached, exit the loop
 
                     // fall through
 
-                case ';':
+                case wxT(';'):
                     // store this field and start looking for the next one
 
                     // trim whitespaces from both sides
                     // store this field and start looking for the next one
 
                     // trim whitespaces from both sides
@@ -1072,9 +2085,9 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                     switch ( currentToken ) {
                         case Field_Type:
                             strType = curField;
                     switch ( currentToken ) {
                         case Field_Type:
                             strType = curField;
-                            if ( strType.Find('/') == wxNOT_FOUND ) {
+                            if ( strType.Find(wxT('/')) == wxNOT_FOUND ) {
                                 // we interpret "type" as "type/*"
                                 // we interpret "type" as "type/*"
-                                strType += "/*";
+                                strType += wxT("/*");
                             }
 
                             currentToken = Field_OpenCmd;
                             }
 
                             currentToken = Field_OpenCmd;
@@ -1092,22 +2105,22 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                                 bool ok = TRUE;
 
                                 // is this something of the form foo=bar?
                                 bool ok = TRUE;
 
                                 // is this something of the form foo=bar?
-                                const char *pEq = strchr(curField, '=');
+                                const wxChar *pEq = wxStrchr(curField, wxT('='));
                                 if ( pEq != NULL ) {
                                 if ( pEq != NULL ) {
-                                    wxString lhs = curField.Before('='),
-                                             rhs = curField.After('=');
+                                    wxString lhs = curField.BeforeFirst(wxT('=')),
+                                             rhs = curField.AfterFirst(wxT('='));
 
                                     lhs.Trim(TRUE);     // from right
                                     rhs.Trim(FALSE);    // from left
 
 
                                     lhs.Trim(TRUE);     // from right
                                     rhs.Trim(FALSE);    // from left
 
-                                    if ( lhs == "print" )
+                                    if ( lhs == wxT("print") )
                                         strPrintCmd = rhs;
                                         strPrintCmd = rhs;
-                                    else if ( lhs == "test" )
+                                    else if ( lhs == wxT("test") )
                                         strTest = rhs;
                                         strTest = rhs;
-                                    else if ( lhs == "description" ) {
+                                    else if ( lhs == wxT("description") ) {
                                         // it might be quoted
                                         // it might be quoted
-                                        if ( rhs[0u] == '"' &&
-                                             rhs.Last() == '"' ) {
+                                        if ( rhs[0u] == wxT('"') &&
+                                             rhs.Last() == wxT('"') ) {
                                             strDesc = wxString(rhs.c_str() + 1,
                                                                rhs.Len() - 2);
                                         }
                                             strDesc = wxString(rhs.c_str() + 1,
                                                                rhs.Len() - 2);
                                         }
@@ -1115,10 +2128,10 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                                             strDesc = rhs;
                                         }
                                     }
                                             strDesc = rhs;
                                         }
                                     }
-                                    else if ( lhs == "compose" ||
-                                              lhs == "composetyped" ||
-                                              lhs == "notes" ||
-                                              lhs == "edit" )
+                                    else if ( lhs == wxT("compose") ||
+                                              lhs == wxT("composetyped") ||
+                                              lhs == wxT("notes") ||
+                                              lhs == wxT("edit") )
                                         ;   // ignore
                                     else
                                         ok = FALSE;
                                         ;   // ignore
                                     else
                                         ok = FALSE;
@@ -1129,11 +2142,11 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                                     // TODO support the flags:
                                     //  1. create an xterm for 'needsterminal'
                                     //  2. append "| $PAGER" for 'copiousoutput'
                                     // TODO support the flags:
                                     //  1. create an xterm for 'needsterminal'
                                     //  2. append "| $PAGER" for 'copiousoutput'
-                                    if ( curField == "needsterminal" )
+                                    if ( curField == wxT("needsterminal") )
                                         needsterminal = TRUE;
                                         needsterminal = TRUE;
-                                    else if ( curField == "copiousoutput" )
+                                    else if ( curField == wxT("copiousoutput") )
                                         copiousoutput = TRUE;
                                         copiousoutput = TRUE;
-                                    else if ( curField == "textualnewlines" )
+                                    else if ( curField == wxT("textualnewlines") )
                                         ;   // ignore
                                     else
                                         ok = FALSE;
                                         ;   // ignore
                                     else
                                         ok = FALSE;
@@ -1141,19 +2154,27 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
 
                                 if ( !ok )
                                 {
 
                                 if ( !ok )
                                 {
-                                    // don't flood the user with error messages
-                                    // if we don't understand something in his
-                                    // mailcap
-                                    wxLogDebug
-                                    (
-                                      _("Mailcap file %s, line %d: unknown "
-                                        "field '%s' for the MIME type "
-                                        "'%s' ignored."),
-                                      strFileName.c_str(),
-                                      nLine + 1,
-                                      curField.c_str(),
-                                      strType.c_str()
-                                    );
+                                    // we don't understand this field, but
+                                    // Netscape stores info in it, so don't warn
+                                    // about it
+                                    if ( curField.Left(16u) != "x-mozilla-flags=" )
+                                    {
+                                        // don't flood the user with error
+                                        // messages if we don't understand
+                                        // something in his mailcap, but give
+                                        // them in debug mode because this might
+                                        // be useful for the programmer
+                                        wxLogDebug
+                                        (
+                                          wxT("Mailcap file %s, line %d: "
+                                              "unknown field '%s' for the "
+                                              "MIME type '%s' ignored."),
+                                              strFileName.c_str(),
+                                              nLine + 1,
+                                              curField.c_str(),
+                                              strType.c_str()
+                                        );
+                                    }
                                 }
                             }
 
                                 }
                             }
 
@@ -1162,7 +2183,7 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                             break;
 
                         default:
                             break;
 
                         default:
-                            wxFAIL_MSG("unknown field type in mailcap");
+                            wxFAIL_MSG(wxT("unknown field type in mailcap"));
                     }
 
                     // next token starts immediately after ';'
                     }
 
                     // next token starts immediately after ';'
@@ -1185,6 +2206,8 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                                                    strPrintCmd,
                                                    strTest);
 
                                                    strPrintCmd,
                                                    strTest);
 
+            // NB: because of complications below (we must get entries priority
+            //     right), we can't use AddMailcapInfo() here, unfortunately.
             strType.MakeLower();
             int nIndex = m_aTypes.Index(strType);
             if ( nIndex == wxNOT_FOUND ) {
             strType.MakeLower();
             int nIndex = m_aTypes.Index(strType);
             if ( nIndex == wxNOT_FOUND ) {
@@ -1192,30 +2215,55 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                 m_aTypes.Add(strType);
 
                 m_aEntries.Add(entry);
                 m_aTypes.Add(strType);
 
                 m_aEntries.Add(entry);
-                m_aExtensions.Add("");
+                m_aExtensions.Add(wxT(""));
                 m_aDescriptions.Add(strDesc);
             }
             else {
                 m_aDescriptions.Add(strDesc);
             }
             else {
-                // modify the existing entry: the entry in one and the same file
-                // are read in top-to-bottom order, i.e. the entries read first
-                // should be tried before the entries below. However, the files
-                // read later should override the settings in the files read
-                // before, thus we Append() the new entry to the list if it has
-                // already occured in _this_ file, but Prepend() it if it
-                // occured in some of the previous ones.
-                if ( aEntryIndices.Index(nIndex) == wxNOT_FOUND ) {
-                    // first time in this file
-                    aEntryIndices.Add(nIndex);
-                    entry->Prepend(m_aEntries[nIndex]);
-                    m_aEntries[nIndex] = entry;
+                // modify the existing entry: the entries in one and the same
+                // file are read in top-to-bottom order, i.e. the entries read
+                // first should be tried before the entries below. However,
+                // the files read later should override the settings in the
+                // files read before (except if fallback is TRUE), thus we
+                // Insert() the new entry to the list if it has already
+                // occured in _this_ file, but Prepend() it if it occured in
+                // some of the previous ones and Append() to it in the
+                // fallback case
+
+                if ( fallback ) {
+                    // 'fallback' parameter prevents the entries from this
+                    // file from overriding the other ones - always append
+                    MailCapEntry *entryOld = m_aEntries[nIndex];
+                    if ( entryOld )
+                        entry->Append(entryOld);
+                    else
+                        m_aEntries[nIndex] = entry;
                 }
                 else {
                 }
                 else {
-                    // not the first time in _this_ file
-                    entry->Append(m_aEntries[nIndex]);
+                    int entryIndex = aEntryIndices.Index(nIndex);
+                    if ( entryIndex == wxNOT_FOUND ) {
+                        // first time in this file
+                        aEntryIndices.Add(nIndex);
+                        aLastIndices.Add(0);
+
+                        entry->Prepend(m_aEntries[nIndex]);
+                        m_aEntries[nIndex] = entry;
+                    }
+                    else {
+                        // not the first time in _this_ file
+                        size_t nEntryIndex = (size_t)entryIndex;
+                        MailCapEntry *entryOld = m_aEntries[nIndex];
+                        if ( entryOld )
+                            entry->Insert(entryOld, aLastIndices[nEntryIndex]);
+                        else
+                            m_aEntries[nIndex] = entry;
+
+                        // the indices were shifted by 1
+                        aLastIndices[nEntryIndex]++;
+                    }
                 }
 
                 if ( !strDesc.IsEmpty() ) {
                 }
 
                 if ( !strDesc.IsEmpty() ) {
-                    // @@ replace the old one - what else can we do??
+                    // replace the old one - what else can we do??
                     m_aDescriptions[nIndex] = strDesc;
                 }
             }
                     m_aDescriptions[nIndex] = strDesc;
                 }
             }
@@ -1226,9 +2274,34 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                   m_aTypes.Count() == m_aExtensions.Count() &&
                   m_aTypes.Count() == m_aDescriptions.Count() );
     }
                   m_aTypes.Count() == m_aExtensions.Count() &&
                   m_aTypes.Count() == m_aDescriptions.Count() );
     }
+
+    return TRUE;
 }
 
 }
 
-#endif // OS type
+size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
+{
+    mimetypes.Empty();
+
+    wxString type;
+    size_t count = m_aTypes.GetCount();
+    for ( size_t n = 0; n < count; n++ )
+    {
+        // don't return template types from here (i.e. anything containg '*')
+        type = m_aTypes[n];
+        if ( type.Find(_T('*')) == wxNOT_FOUND )
+        {
+            mimetypes.Add(type);
+        }
+    }
+
+    return mimetypes.GetCount();
+}
+
+#endif
+  // OS type
+
+#endif
+  // wxUSE_FILE && wxUSE_TEXTFILE
 
 #endif
   // __WIN16__
 
 #endif
   // __WIN16__