From 8e124873c5bd39ba8e9cc3c05ac7e4c97fbe59cd Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Sat, 7 Aug 1999 23:10:09 +0000
Subject: [PATCH] wxMimeTypesManager::AddFallbacks() added, also corrected a
 minor bug/incompatible behaviour in wxFileType and added a MIME database
 query demo to typetest sample

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@3312 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/mimetype.tex    |  14 +-
 include/wx/mimetype.h         |  16 +-
 samples/typetest/typetest.cpp | 252 ++++++++++++++++++---------
 samples/typetest/typetest.h   |  46 +++--
 src/common/mimetype.cpp       | 317 +++++++++++++++++++++++++++++-----
 5 files changed, 489 insertions(+), 156 deletions(-)

diff --git a/docs/latex/wx/mimetype.tex b/docs/latex/wx/mimetype.tex
index bd7275d5b9..822e0a553c 100644
--- a/docs/latex/wx/mimetype.tex
+++ b/docs/latex/wx/mimetype.tex
@@ -70,7 +70,8 @@ default ones which are loaded automatically) containing MIME
 information in either mailcap(5) or mime.types(5) format.
 
 \helpref{ReadMailcap}{wxmimetypesmanagerreadmailcap}\\
-\helpref{ReadMimeTypes}{wxmimetypesmanagerreadmimetypes}
+\helpref{ReadMimeTypes}{wxmimetypesmanagerreadmimetypes}\\
+\helpref{AddFallbacks}{wxmimetypesmanageraddfallbacks}
 
 %%%%% MEMBERS HERE %%%%%
 \helponly{\insertatlevel{2}{
@@ -93,6 +94,17 @@ additional mailcap/mime.types files.
 
 Destructor is not virtual, so this class should not be derived from.
 
+\membersection{wxMimeTypesManager::AddFallbacks}\label{wxmimetypesmanageraddfallbacks}
+
+\func{void}{AddFallbacks}{\param{const wxFileTypeInfo *}{fallbacks}}
+
+This function may be used to provdie hard-wired fallbacks for the MIME types
+and extensions that might not be present in the system MIME database.
+
+% TODO
+
+Please see the typetest sample for an example of using it.
+
 \membersection{wxMimeTypesManager::GetFileTypeFromExtension}\label{wxmimetypesmanagergetfiletypefromextension}
 
 \func{wxFileType*}{GetFileTypeFromExtension}{\param{const wxString\&}{ extension}}
diff --git a/include/wx/mimetype.h b/include/wx/mimetype.h
index e71b6c3890..03445fa3da 100644
--- a/include/wx/mimetype.h
+++ b/include/wx/mimetype.h
@@ -120,13 +120,25 @@ public:
                    // the other parameters form a NULL terminated list of
                    // extensions
                    ...);
-                   
+
         // invalid item - use this to terminate the array passed to
         // wxMimeTypesManager::AddFallbacks
     wxFileTypeInfo() { }
 
     bool IsValid() const { return !m_mimeType.IsEmpty(); }
 
+    // accessors
+        // get the MIME type
+    const wxString& GetMimeType() const { return m_mimeType; }
+        // get the open command
+    const wxString& GetOpenCommand() const { return m_openCmd; }
+        // get the print command
+    const wxString& GetPrintCommand() const { return m_printCmd; }
+        // get the description
+    const wxString& GetDescription() const { return m_desc; }
+        // get the array of all extensions
+    const wxArrayString& GetExtensions() const { return m_exts; }
+
 private:
     wxString m_mimeType,    // the MIME type in "type/subtype" form
              m_openCmd,     // command to use for opening the file (%s allowed)
@@ -187,7 +199,7 @@ public:
     // function.
     //
     // The filetypes array should be terminated by a NULL entry
-    bool AddFallbacks(const wxFileTypeInfo *filetypes);
+    void AddFallbacks(const wxFileTypeInfo *filetypes);
 
     // dtor (not virtual, shouldn't be derived from)
     ~wxMimeTypesManager();
diff --git a/samples/typetest/typetest.cpp b/samples/typetest/typetest.cpp
index 4cdeff9cbb..ed81e14691 100644
--- a/samples/typetest/typetest.cpp
+++ b/samples/typetest/typetest.cpp
@@ -6,7 +6,7 @@
 // Created:     04/01/98
 // RCS-ID:      $Id$
 // Copyright:   (c) Julian Smart and Markus Holzem
-// Licence:   	wxWindows license
+// Licence:       wxWindows license
 /////////////////////////////////////////////////////////////////////////////
 
 #ifdef __GNUG__
@@ -27,6 +27,7 @@
 #include "wx/time.h"
 #include "wx/date.h"
 #include "wx/variant.h"
+#include "wx/mimetype.h"
 
 #include "typetest.h"
 
@@ -47,68 +48,74 @@
 #include "wx/txtstrm.h"
 
 // Create a new application object
-IMPLEMENT_APP	(MyApp)
+IMPLEMENT_APP    (MyApp)
 
-IMPLEMENT_DYNAMIC_CLASS	(MyApp, wxApp)
+IMPLEMENT_DYNAMIC_CLASS    (MyApp, wxApp)
 
 BEGIN_EVENT_TABLE(MyApp, wxApp)
-	EVT_MENU(TYPES_DATE,      MyApp::DoDateDemo)
-	EVT_MENU(TYPES_TIME,      MyApp::DoTimeDemo)
-	EVT_MENU(TYPES_VARIANT,   MyApp::DoVariantDemo)
-	EVT_MENU(TYPES_BYTEORDER, MyApp::DoByteOrderDemo)
+    EVT_MENU(TYPES_DATE,      MyApp::DoDateDemo)
+    EVT_MENU(TYPES_TIME,      MyApp::DoTimeDemo)
+    EVT_MENU(TYPES_VARIANT,   MyApp::DoVariantDemo)
+    EVT_MENU(TYPES_BYTEORDER, MyApp::DoByteOrderDemo)
 #if wxUSE_UNICODE
-	EVT_MENU(TYPES_UNICODE,   MyApp::DoUnicodeDemo)
+    EVT_MENU(TYPES_UNICODE,   MyApp::DoUnicodeDemo)
 #endif
-	EVT_MENU(TYPES_STREAM, MyApp::DoStreamDemo)
+    EVT_MENU(TYPES_STREAM, MyApp::DoStreamDemo)
+    EVT_MENU(TYPES_MIME, MyApp::DoMIMEDemo)
 END_EVENT_TABLE()
 
-bool MyApp::OnInit(void)
+bool MyApp::OnInit()
 {
-  // Create the main frame window
-  MyFrame *frame = new MyFrame((wxFrame *) NULL, "wxWindows Types Demo",
-      wxPoint(50, 50), wxSize(450, 340));
-
-  // Give it an icon
-  frame->SetIcon(wxICON(mondrian));
-
-  // Make a menubar
-  wxMenu *file_menu = new wxMenu;
-
-  file_menu->Append(TYPES_ABOUT, "&About");
-  file_menu->AppendSeparator();
-  file_menu->Append(TYPES_DATE, "&Date test");
-  file_menu->Append(TYPES_TIME, "&Time test");
-  file_menu->Append(TYPES_VARIANT, "&Variant test");
-  file_menu->Append(TYPES_BYTEORDER, "&Byteorder test");
+    // Create the main frame window
+    MyFrame *frame = new MyFrame((wxFrame *) NULL, "wxWindows Types Demo",
+                                 wxPoint(50, 50), wxSize(450, 340));
+
+    // Give it an icon
+    frame->SetIcon(wxICON(mondrian));
+
+    // Make a menubar
+    wxMenu *file_menu = new wxMenu;
+
+    file_menu->Append(TYPES_ABOUT, "&About");
+    file_menu->AppendSeparator();
+    file_menu->Append(TYPES_QUIT, "E&xit\tAlt-X");
+
+    wxMenu *test_menu = new wxMenu;
+    test_menu->Append(TYPES_DATE, "&Date test");
+    test_menu->Append(TYPES_TIME, "&Time test");
+    test_menu->Append(TYPES_VARIANT, "&Variant test");
+    test_menu->Append(TYPES_BYTEORDER, "&Byteorder test");
 #if wxUSE_UNICODE
-  file_menu->Append(TYPES_UNICODE, "&Unicode test");
+    test_menu->Append(TYPES_UNICODE, "&Unicode test");
 #endif
-  file_menu->Append(TYPES_STREAM, "&Stream test");
-  file_menu->AppendSeparator();
-  file_menu->Append(TYPES_QUIT, "E&xit");
-  wxMenuBar *menu_bar = new wxMenuBar;
-  menu_bar->Append(file_menu, "&File");
-  frame->SetMenuBar(menu_bar);
+    test_menu->Append(TYPES_STREAM, "&Stream test");
+    test_menu->AppendSeparator();
+    test_menu->Append(TYPES_MIME, "&MIME database test");
 
-  m_textCtrl = new wxTextCtrl(frame, -1, "", wxPoint(0, 0), wxDefaultSize, wxTE_MULTILINE);
+    wxMenuBar *menu_bar = new wxMenuBar;
+    menu_bar->Append(file_menu, "&File");
+    menu_bar->Append(test_menu, "&Tests");
+    frame->SetMenuBar(menu_bar);
 
-  // Show the frame
-  frame->Show(TRUE);
-  
-  SetTopWindow(frame);
+    m_textCtrl = new wxTextCtrl(frame, -1, "", wxPoint(0, 0), wxDefaultSize, wxTE_MULTILINE);
 
-  return TRUE;
+    // Show the frame
+    frame->Show(TRUE);
+
+    SetTopWindow(frame);
+
+    return TRUE;
 }
 
 void MyApp::DoStreamDemo(wxCommandEvent& WXUNUSED(event))
 {
     wxTextCtrl& textCtrl = * GetTextCtrl();
-    
+
     textCtrl.Clear();
     textCtrl << _T("\nTest fstream vs. wxFileStream:\n\n");
 
     textCtrl.WriteText( "Writing to ofstream and wxFileOutputStream:\n" );
-    
+
     ofstream std_file_output( "test_std.dat" );
     wxFileOutputStream file_output( "test_wx.dat" );
     wxBufferedOutputStream buf_output( file_output );
@@ -120,122 +127,122 @@ void MyApp::DoStreamDemo(wxCommandEvent& WXUNUSED(event))
     textCtrl.WriteText( tmp );
     text_output << si << "\n";
     std_file_output << si << "\n";
-    
+
     unsigned int ui = 0xFFFFFFFF;
     tmp.Printf( _T("Unsigned int: %u\n"), ui );
     textCtrl.WriteText( tmp );
     text_output << ui << "\n";
     std_file_output << ui << "\n";
-    
+
     double d = 2.01234567890123456789;
     tmp.Printf( _T("Double: %f\n"), d );
     textCtrl.WriteText( tmp );
     text_output << d << "\n";
     std_file_output << d << "\n";
-    
-    float f = 0.00001;
+
+    float f = (float)0.00001;
     tmp.Printf( _T("Float: %f\n"), f );
     textCtrl.WriteText( tmp );
     text_output << f << "\n";
     std_file_output << f << "\n";
-    
+
     wxString str( _T("Hello!") );
     tmp.Printf( _T("String: %s\n"), str.c_str() );
     textCtrl.WriteText( tmp );
     text_output << str << "\n";
     std_file_output << str.c_str() << "\n";
-    
+
     textCtrl.WriteText( "\nReading from ifstream:\n" );
-    
+
     ifstream std_file_input( "test_std.dat" );
 
     std_file_input >> si;
     tmp.Printf( _T("Signed int: %d\n"), si );
     textCtrl.WriteText( tmp );
-    
+
     std_file_input >> ui;
     tmp.Printf( _T("Unsigned int: %u\n"), ui );
     textCtrl.WriteText( tmp );
-    
+
     std_file_input >> d;
     tmp.Printf( _T("Double: %f\n"), d );
     textCtrl.WriteText( tmp );
-    
+
     std_file_input >> f;
     tmp.Printf( _T("Float: %f\n"), f );
     textCtrl.WriteText( tmp );
-    
+
     std_file_input >> str;
     tmp.Printf( _T("String: %s\n"), str.c_str() );
     textCtrl.WriteText( tmp );
-    
+
     textCtrl.WriteText( "\nReading from wxFileInputStream:\n" );
 
     buf_output.Sync();
-    
+
     wxFileInputStream file_input( "test_wx.dat" );
     wxBufferedInputStream buf_input( file_input );
     wxTextInputStream text_input( buf_input );
-    
+
     text_input >> si;
     tmp.Printf( _T("Signed int: %d\n"), si );
     textCtrl.WriteText( tmp );
-    
+
     text_input >> ui;
     tmp.Printf( _T("Unsigned int: %u\n"), ui );
     textCtrl.WriteText( tmp );
-    
+
     text_input >> d;
     tmp.Printf( _T("Double: %f\n"), d );
     textCtrl.WriteText( tmp );
-    
+
     text_input >> f;
     tmp.Printf( _T("Float: %f\n"), f );
     textCtrl.WriteText( tmp );
-    
+
     text_input >> str;
     tmp.Printf( _T("String: %s\n"), str.c_str() );
     textCtrl.WriteText( tmp );
-    
+
 
     textCtrl << "\nTest for wxDataStream:\n\n";
 
     textCtrl.WriteText( "Writing to wxDataOutputStream:\n" );
-    
+
     file_output.SeekO( 0 );
     wxDataOutputStream data_output( buf_output );
 
-    wxInt16 i16 = 0xFFFF;
+    wxInt16 i16 = (short)0xFFFF;
     tmp.Printf( _T("Signed int16: %d\n"), (int)i16 );
     textCtrl.WriteText( tmp );
     data_output.Write16( i16 );
-    
+
     wxUint16 ui16 = 0xFFFF;
     tmp.Printf( _T("Unsigned int16: %u\n"), (unsigned int) ui16 );
     textCtrl.WriteText( tmp );
     data_output.Write16( ui16 );
-    
+
     d = 2.01234567890123456789;
     tmp.Printf( _T("Double: %f\n"), d );
     textCtrl.WriteText( tmp );
     data_output.WriteDouble( d );
-    
+
     str = "Hello!";
     tmp.Printf( _T("String: %s\n"), str.c_str() );
     textCtrl.WriteText( tmp );
     data_output.WriteString( str );
-    
+
     buf_output.Sync();
-    
+
     textCtrl.WriteText( "\nReading from wxDataInputStream:\n" );
-    
+
     file_input.SeekI( 0 );
     wxDataInputStream data_input( buf_input );
 
     i16 = data_input.Read16();
     tmp.Printf( _T("Signed int16: %d\n"), (int)i16 );
     textCtrl.WriteText( tmp );
-    
+
     ui16 = data_input.Read16();
     tmp.Printf( _T("Unsigned int16: %u\n"), (unsigned int) ui16 );
     textCtrl.WriteText( tmp );
@@ -243,7 +250,7 @@ void MyApp::DoStreamDemo(wxCommandEvent& WXUNUSED(event))
     d = data_input.ReadDouble();
     tmp.Printf( _T("Double: %f\n"), d );
     textCtrl.WriteText( tmp );
-    
+
     str = data_input.ReadString();
     tmp.Printf( _T("String: %s\n"), str.c_str() );
     textCtrl.WriteText( tmp );
@@ -253,55 +260,123 @@ void MyApp::DoStreamDemo(wxCommandEvent& WXUNUSED(event))
 void MyApp::DoUnicodeDemo(wxCommandEvent& WXUNUSED(event))
 {
     wxTextCtrl& textCtrl = * GetTextCtrl();
-    
+
     textCtrl.Clear();
     textCtrl << "\nTest wchar_t to char (Unicode to ANSI/Multibyte) converions:";
 
     wxString str;
     str = _T("Robert Röbling\n");
-    
+
     printf( "\n\nConversion with wxConvLocal:\n" );
     wxConvCurrent = &wxConvLocal;
     printf( (const char*) str.mbc_str() );
-    
+
     printf( "\n\nConversion with wxConvGdk:\n" );
     wxConvCurrent = &wxConvGdk;
     printf( (const char*) str.mbc_str() );
-    
+
     printf( "\n\nConversion with wxConvLibc:\n" );
     wxConvCurrent = &wxConvLibc;
     printf( (const char*) str.mbc_str() );
-    
+
 }
 #endif
 
+void MyApp::DoMIMEDemo(wxCommandEvent& WXUNUSED(event))
+{
+    static wxString s_defaultExt = "xyz";
+
+    wxString ext = wxGetTextFromUser("Enter a file extension: ",
+                                     "MIME database test",
+                                     s_defaultExt);
+    if ( !!ext )
+    {
+        s_defaultExt = ext;
+
+        // init MIME database if not done yet
+        if ( !m_mimeDatabase )
+        {
+            m_mimeDatabase = new wxMimeTypesManager;
+
+            static const wxFileTypeInfo fallbacks[] =
+            {
+                wxFileTypeInfo("application/xyz",
+                               "XyZ %s",
+                               "XyZ -p %s",
+                               "The one and only XYZ format file",
+                               "xyz", "123", NULL),
+                wxFileTypeInfo("text/html",
+                               "lynx %s",
+                               "lynx -dump %s | lpr",
+                               "HTML document (from fallback)",
+                               "htm", "html", NULL),
+
+                // must terminate the table with this!
+                wxFileTypeInfo()
+            };
+
+            m_mimeDatabase->AddFallbacks(fallbacks);
+        }
+
+        wxTextCtrl& textCtrl = * GetTextCtrl();
+
+        wxFileType *filetype = m_mimeDatabase->GetFileTypeFromExtension(ext);
+        if ( !filetype )
+        {
+            textCtrl << "Unknown extension '" << ext << "'\n";
+        }
+        else
+        {
+            wxString type, desc, open;
+            filetype->GetMimeType(&type);
+            filetype->GetDescription(&desc);
+
+            wxString filename = "filename";
+            filename << "." << ext;
+            wxFileType::MessageParameters params(filename, type);
+            filetype->GetOpenCommand(&open, params);
+
+            textCtrl << "MIME information about extension '" << ext << "'\n"
+                     << "\tMIME type: " << ( !type ? "unknown"
+                                                   : type.c_str() ) << '\n'
+                     << "\tDescription: " << ( !desc ? "" : desc.c_str() )
+                        << '\n'
+                     << "\tCommand to open: " << ( !open ? "no" : open.c_str() )
+                        << '\n';
+
+            delete filetype;
+        }
+    }
+    //else: cancelled by user
+}
+
 void MyApp::DoByteOrderDemo(wxCommandEvent& WXUNUSED(event))
 {
     wxTextCtrl& textCtrl = * GetTextCtrl();
 
     textCtrl.Clear();
     textCtrl << "\nTest byte order macros:\n\n";
-    
+
     if (wxBYTE_ORDER == wxLITTLE_ENDIAN)
         textCtrl << "This is a little endian system.\n\n";
-    else    
+    else
         textCtrl << "This is a big endian system.\n\n";
-	
+
     wxString text;
-    
+
     wxInt32 var = 0xF1F2F3F4;
     text = "";
     text.Printf( _T("Value of wxInt32 is now: %#x.\n\n"), var );
     textCtrl.WriteText( text );
-    
+
     text = "";
     text.Printf( _T("Value of swapped wxInt32 is: %#x.\n\n"), wxINT32_SWAP_ALWAYS( var ) );
     textCtrl.WriteText( text );
-    
+
     text = "";
     text.Printf( _T("Value of wxInt32 swapped on little endian is: %#x.\n\n"), wxINT32_SWAP_ON_LE( var ) );
     textCtrl.WriteText( text );
-    
+
     text = "";
     text.Printf( _T("Value of wxInt32 swapped on big endian is: %#x.\n\n"), wxINT32_SWAP_ON_BE( var ) );
     textCtrl.WriteText( text );
@@ -346,7 +421,6 @@ void MyApp::DoDateDemo(wxCommandEvent& WXUNUSED(event))
     a = a - 25;
     textCtrl << a.FormatDate(wxEUROPEAN) << "  (European)\n";
 
-    
     // Using subtraction of two date objects
     wxDate a1 = wxString("7/13/1991");
     wxDate a2 = a1 + 14;
@@ -481,6 +555,12 @@ void MyApp::DoVariantDemo(wxCommandEvent& WXUNUSED(event) )
     // Implicit conversion
     long l = var1;
 
+    // suppress compile warnings about unused variables
+    if ( l < v )
+    {
+        ;
+    }
+
     wxStringList stringList;
     stringList.Add(_T("one")); stringList.Add(_T("two")); stringList.Add(_T("three"));
     var1 = stringList;
@@ -502,8 +582,8 @@ void MyApp::DoVariantDemo(wxCommandEvent& WXUNUSED(event) )
 }
 
 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
-	EVT_MENU(TYPES_QUIT, MyFrame::OnQuit)
-	EVT_MENU(TYPES_ABOUT, MyFrame::OnAbout)
+    EVT_MENU(TYPES_QUIT, MyFrame::OnQuit)
+    EVT_MENU(TYPES_ABOUT, MyFrame::OnAbout)
 END_EVENT_TABLE()
 
 // My frame constructor
@@ -520,7 +600,7 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event) )
 {
   wxMessageDialog dialog(this, "Tests various wxWindows types",
-  	"About Types", wxYES_NO|wxCANCEL);
+      "About Types", wxYES_NO|wxCANCEL);
 
   dialog.ShowModal();
 }
diff --git a/samples/typetest/typetest.h b/samples/typetest/typetest.h
index ae37f643fb..a8f6124b38 100644
--- a/samples/typetest/typetest.h
+++ b/samples/typetest/typetest.h
@@ -6,7 +6,7 @@
 // Created:     04/01/98
 // RCS-ID:      $Id$
 // Copyright:   (c) Julian Smart
-// Licence:   	wxWindows licence
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 #ifdef __GNUG__
@@ -20,7 +20,10 @@
 class MyApp: public wxApp
 {
 public:
-    bool OnInit(void);
+    MyApp() { m_textCtrl = NULL; m_mimeDatabase = NULL; }
+
+    bool OnInit();
+    int OnExit() { delete m_mimeDatabase; return wxApp::OnExit(); }
 
     void DoDateDemo(wxCommandEvent& event);
     void DoTimeDemo(wxCommandEvent& event);
@@ -30,14 +33,16 @@ public:
 #if wxUSE_UNICODE
     void DoUnicodeDemo(wxCommandEvent& event);
 #endif
+    void DoMIMEDemo(wxCommandEvent& event);
 
-    wxTextCtrl*     GetTextCtrl() const { return m_textCtrl; }
+    wxTextCtrl* GetTextCtrl() const { return m_textCtrl; }
 
 private:
-    wxTextCtrl*     m_textCtrl;
+    wxTextCtrl* m_textCtrl;
+    wxMimeTypesManager *m_mimeDatabase;
 
-DECLARE_DYNAMIC_CLASS(MyApp)
-DECLARE_EVENT_TABLE()
+    DECLARE_DYNAMIC_CLASS(MyApp)
+    DECLARE_EVENT_TABLE()
 };
 
 DECLARE_APP(MyApp)
@@ -48,26 +53,29 @@ class MyFrame: public wxFrame
 public:
     MyFrame(wxFrame *parent, const wxString& title,
             const wxPoint& pos, const wxSize& size);
-    
+
 public:
     void OnQuit(wxCommandEvent& event);
     void OnAbout(wxCommandEvent& event);
 
-   DECLARE_EVENT_TABLE()
-    
+    DECLARE_EVENT_TABLE()
 };
 
 // ID for the menu commands
-#define TYPES_QUIT 	    wxID_EXIT
-#define TYPES_TEXT 	    101
-#define TYPES_ABOUT     102
-
-#define TYPES_DATE      103
-#define TYPES_TIME      104
-#define TYPES_VARIANT   105
-#define TYPES_BYTEORDER 106
-#define TYPES_UNICODE   107
-#define TYPES_STREAM    108
+enum
+{
+    TYPES_QUIT = wxID_EXIT,
+    TYPES_TEXT = 101,
+    TYPES_ABOUT,
+
+    TYPES_DATE,
+    TYPES_TIME,
+    TYPES_VARIANT,
+    TYPES_BYTEORDER,
+    TYPES_UNICODE,
+    TYPES_STREAM,
+    TYPES_MIME
+};
 
 #endif
     // _WX_TYPETEST_H_
diff --git a/src/common/mimetype.cpp b/src/common/mimetype.cpp
index 2ff0df4d34..297c41673e 100644
--- a/src/common/mimetype.cpp
+++ b/src/common/mimetype.cpp
@@ -83,13 +83,20 @@ 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);
@@ -97,19 +104,25 @@ public:
     bool GetIcon(wxIcon *icon) const;
     bool GetDescription(wxString *desc) const;
     bool GetOpenCommand(wxString *openCmd,
-                        const wxFileType::MessageParameters&) const
-        { return GetCommand(openCmd, _T("open")); }
+                        const wxFileType::MessageParameters& params) const;
     bool GetPrintCommand(wxString *printCmd,
-                         const wxFileType::MessageParameters&) const
-        { return GetCommand(printCmd, _T("print")); }
+                         const wxFileType::MessageParameters& params) const;
 
 private:
-    // helper function
-    bool GetCommand(wxString *command, const wxChar *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,
+                    m_ext;
 };
 
+WX_DECLARE_OBJARRAY(wxFileTypeInfo, wxArrayFileTypeInfo);
+#include "wx/arrimpl.cpp"
+WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo);
+
 class wxMimeTypesManagerImpl
 {
 public:
@@ -126,6 +139,11 @@ public:
         { return TRUE; }
     bool ReadMimeTypes(const wxString& filename)
         { return TRUE; }
+
+    void AddFallback(const wxFileTypeInfo& ft) { m_fallbacks.Add(ft); }
+
+private:
+    wxArrayFileTypeInfo m_fallbacks;
 };
 
 #else  // Unix
@@ -278,6 +296,18 @@ public:
     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
@@ -333,6 +363,42 @@ private:
 
 #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
 // ============================================================================
@@ -518,13 +584,20 @@ 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);
+    }
+}
+
 // ============================================================================
 // real (OS specific) implementation
 // ============================================================================
 
 #ifdef __WXMSW__
 
-bool wxFileTypeImpl::GetCommand(wxString *command, const wxChar *verb) const
+wxString wxFileTypeImpl::GetCommand(const wxChar *verb) const
 {
     // suppress possible error messages
     wxLogNull nolog;
@@ -532,40 +605,84 @@ bool wxFileTypeImpl::GetCommand(wxString *command, const wxChar *verb) const
     strKey << m_strFileType << _T("\\shell\\") << verb << _T("\\command");
     wxRegKey key(wxRegKey::HKCR, strKey);
 
+    wxString command;
     if ( key.Open() ) {
         // it's the default value of the key
-        if ( key.QueryValue(_T(""), *command) ) {
+        if ( key.QueryValue(_T(""), command) ) {
             // transform it from '%1' to '%s' style format string
+
             // 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'!
-            size_t len = command->Len();
-            for ( size_t n = 0; n < len; n++ ) {
-                if ( command->GetChar(n) == _T('%') &&
-                     (n + 1 < len) && command->GetChar(n + 1) == _T('1') ) {
+            bool foundFilename = FALSE;
+            size_t len = command.Len();
+            for ( size_t n = 0; (n < len) && !foundFilename; n++ ) {
+                if ( command[n] == _T('%') &&
+                     (n + 1 < len) && command[n + 1] == _T('1') ) {
                     // replace it with '%s'
-                    command->SetChar(n + 1, _T('s'));
+                    command[n + 1] = _T('s');
 
-                    return TRUE;
+                    foundFilename = TRUE;
                 }
             }
 
-            // we didn't find any '%1'!
-            // HACK: append the filename at the end, hope that it will do
-            *command << _T(" %s");
-
-            return TRUE;
+            if ( !foundFilename ) {
+                // we didn't find any '%1'!
+                // HACK: append the filename at the end, hope that it will do
+                command << _T(" %s");
+            }
         }
     }
 
     // no such file type or no value
-    return FALSE;
+    return command;
+}
+
+bool
+wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
+                               const wxFileType::MessageParameters& params)
+                               const
+{
+    wxString cmd;
+    if ( m_info ) {
+        cmd = m_info->GetOpenCommand();
+    }
+    else {
+        cmd = GetCommand(_T("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(_T("print"));
+    }
+
+    *printCmd = wxFileType::ExpandCommand(cmd, params);
+
+    return !printCmd->IsEmpty();
 }
 
 // TODO this function is half implemented
 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;
@@ -581,6 +698,13 @@ bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
 
 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;
     wxRegKey key(wxRegKey::HKCR, /*m_strFileType*/ _T(".") + m_ext);
@@ -594,6 +718,11 @@ bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
 
 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
 {
+    if ( m_info ) {
+        // we don't have icons in the fallback resources
+        return FALSE;
+    }
+
     wxString strIconKey;
     strIconKey << m_strFileType << _T("\\DefaultIcon");
 
@@ -642,6 +771,13 @@ bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
 
 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);
@@ -677,8 +813,20 @@ wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
         if ( key.QueryValue(_T(""), strFileType) ) {
             // 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;
+        }
+    }
+
+    // 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;
         }
@@ -710,6 +858,20 @@ wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
         }
     }
 
+    // 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;
 }
@@ -903,6 +1065,77 @@ wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
     }
 }
 
+void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
+{
+    const wxArrayString& exts = filetype.GetExtensions();
+    size_t nExts = exts.GetCount();
+    for ( size_t nExt = 0; nExt < nExts; nExt++ ) {
+        if ( nExt > 0 ) {
+            extensions += _T(' ');
+        }
+        extensions += exts[nExt];
+    }
+
+    AddMimeTypeInfo(filetype.GetMimeType(),
+                    extensions,
+                    filetype.GetDescription());
+
+    AddMailcapInfo(filetype.GetMimeType(),
+                   filetype.GetOpenCommand(),
+                   filetype.GetPrintCommand(),
+                   _T(""),
+                   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(_T(""));
+        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(_T("--- Parsing mime.types file '%s' ---"), strFileName.c_str());
@@ -1042,21 +1275,7 @@ bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
             strExtensions.erase(0, 1);
         }
 
-        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;
-        }
+        AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
 
         // finished with this line
         pc = NULL;
@@ -1269,6 +1488,8 @@ bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
                                                    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 ) {
@@ -1339,10 +1560,10 @@ bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
     return TRUE;
 }
 
-#endif 
+#endif
   // OS type
 
-#endif  
+#endif
   // wxUSE_FILE && wxUSE_TEXTFILE
 
 #endif
-- 
2.50.0

