--- /dev/null
+\section{\class{wxFileType}}\label{wxfiletype}
+
+This class holds information about a given "file type". File type is the same as
+MIME type under Unix, but under Windows it corresponds more to an extension than
+to MIME type (in fact, several extensions may correspond to a file type). This
+object may be created in several different ways: the program might know the file
+extension and wish to find out the corresponding MIME type or, conversely, it
+might want to find the right extension for the file to which it writes the
+contents of given MIME type. Depending on how it was created some fields may be
+unknown so the return value of all the accessors {\bf must} be checked: FALSE
+will be returned if the corresponding information couldn't be found.
+
+The objects of this class are never created by the application code but are
+returned by \helpref{wxMimeTypesManager::GetFileTypeFromMimeType}{wxmimetypesmanagergetfiletypefrommimetype} and
+\helpref{wxMimeTypesManager::GetFileTypeFromExtension}{wxmimetypesmanagergetfiletypefromextension} methods.
+But it's your responsability to delete the returned pointer when you're done
+with it!
+
+% TODO describe MIME types better than this...
+A brief remainder about what the MIME types are (see the RFC 1341 for more
+information): basicly, it is just a pair category/type (for example,
+"text/plain") where the category is a basic indication of what a file is
+(examples of categories are "application", "image", "text", "binary"...) and
+type is a precise definition of the document format: "plain" in the example
+above means just ASCII text without any formatting, while "text/html" is the
+HTML document source.
+
+A MIME type may have one or more associated extensions: "text/plain" will
+typically correspond to the extension ".txt", but may as well be associated with
+".ini" or ".conf".
+
+\wxheading{Required headers}
+
+#include <wx/mimetype.h>
+
+\wxheading{Derived from}
+
+No base class.
+
+\wxheading{See also}
+
+\helpref{wxMimeTypesManager}{wxmimetypesmanager}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{MessageParameters class}{wxfiletypemessageparameters}
+
+One of the most common usages of MIME is to encode an e-mail message. The MIME
+type of the encoded message is an example of a {\it message parameter}. These
+parameters are found in the message headers ("Content-XXX"). At the very least,
+they must specify the MIME type and the version of MIME used, but almost always
+they provide additional information about the message such as the original file
+name or the charset (for the text documents).
+
+These parameters may be useful to the program used to open, edit, view or print
+the message, so, for example, an e-mail client program will have to pass them to
+this program. Because wxFileType itself can not know about these parameters,
+it uses MessageParameters class to query them. The default implementation only
+requiers the caller to provide the file name (always used by the program to be
+called - it must know which file to open) and the MIME type and supposes that
+there are no other parameters. If you wish to supply additional parameters, you
+must derive your own class from MessageParameters and override GetParamValue()
+function, for example:
+
+\begin{verbatim}
+// provide the message parameters for the MIME type manager
+class MailMessageParamaters : public wxFileType::MessageParameters
+{
+public:
+ MailMessageParamaters(const wxString& filename,
+ const wxString& mimetype)
+ : wxFileType::MessageParameters(filename, mimetype)
+ {
+ }
+
+ virtual wxString GetParamValue(const wxString& name) const
+ {
+ // parameter names are not case-sensitive
+ if ( name.CmpNoCase("charset") == 0 )
+ return "US-ASCII";
+ else
+ return wxFileType::MessageParameters::GetParamValue(name);
+ }
+};
+\end{verbatim}
+
+Now you only need to create an object of this class and pass it to, for example,
+\helpref{GetOpenCommand}{wxfiletypegetopencommand} like this:
+
+\begin{verbatim}
+wxString command;
+if ( filetype->GetOpenCommand(&command,
+ MailMessageParamaters("foo.txt", "text/plain")) )
+{
+ // the full command for opening the text documents is in 'command'
+ // (it might be "notepad foo.txt" under Windows or "cat foo.txt" under Unix)
+}
+else
+{
+ // we don't know how to handle such files...
+}
+
+\end{verbatim}
+
+{\bf Windows:} As only the file name is used by the program associated with the
+given extension anyhow (but no other message parameters), there is no need to
+ever derive from MessageParameters class for a Windows-only program.
+
+\membersection{wxFileType::wxFileType}\label{wxfiletypewxfiletype}
+\func{}{wxFileType}{\void}
+
+The default constructor is private because you should never create objects of
+this type: they are only returned by
+\helpref{wxMimeTypesManager}{wxmimetypesmanager} methods.
+
+\membersection{wxFileType::\destruct{wxFileType}}\label{wxfiletypedtor}
+\func{}{\destruct{wxFileType}{\void}
+
+The destructor of this class is not virtual, so it should not be derived from.
+
+\membersection{wxFileType::GetMimeType}\label{wxfiletypegetmimetype}
+\func{bool}{GetMimeType}{\param{wxString *}{mimeType}}
+
+If the function returns TRUE, the string pointed to by {\it mimeType} is filled
+with full MIME type specification for this file type: for example, "text/plain".
+
+\membersection{wxFileType::GetExtensions}\label{wxfiletypegetextensions}
+\func{bool}{GetExtensions}{\param{wxArrayString \&}{extensions}}
+
+If the function returns TRUE, the array {\it extensions} is filled
+with all extensions associated with this file type: for example, it may
+contain the following two elements for the MIME type "text/html" (notice the
+absence of the leading dot): "html" and "htm".
+
+{\bf Windows:} This function is currently not implemented: there is no
+(efficient) way to retrieve associated extensions from the given MIME type on
+this platform, so it will only return TRUE if the wxFileType object was created
+by \helpref{GetFileTypeFromExtension}{wxmimetypesmanagergetfiletypefromextension}
+function in the first place.
+
+\membersection{wxFileType::GetIcon}\label{wxfiletypegeticon}
+\func{bool}{GetIcon}{\param{wxIcon *}{icon}}
+
+If the function returns TRUE, the icon associated with this file type will be
+created and assigned to the {\it icon} parameter.
+
+{\bf Unix:} This function always returns FALSE under Unix.
+
+\membersection{wxFileType::GetDescription}\label{wxfiletypegetdescription}
+\func{bool}{GetDescription}{\param{wxString *}{desc}}
+
+If the function returns TRUE, the string pointed to by {\it desc} is filled
+with a brief description for this file type: for example, "text document" for
+the "text/plain" MIME type.
+
+\membersection{wxFileType::GetOpenCommand}\label{wxfiletypegetopencommand}
+\func{bool}{GetOpenCommand}{\param{wxString *}{command},\param{MessageParameters \&}{params}}
+
+If the function returns TRUE, the string pointed to by {\it command} is filled
+with the command which must be executed (see \helpref{wxExecute}{wxexecute}) in
+order to open the file of the given type. The name of the file is
+retrieved from \helpref{MessageParameters}{wxfiletypemessageparameters} class.
+
+\membersection{wxFileType::GetPrintCommand}\label{wxfiletypegetprintcommand}
+\func{bool}{GetPrintCommand}{\param{wxString *}{command},\param{MessageParameters \&}{params}}
+
+If the function returns TRUE, the string pointed to by {\it command} is filled
+with the command which must be executed (see \helpref{wxExecute}{wxexecute}) in
+order to print the file of the given type. The name of the file is
+retrieved from \helpref{MessageParameters}{wxfiletypemessageparameters} class.
+
+\membersection{wxFileType::ExpandCommand}\label{wxfiletypeexpandcommand}
+\func{static wxString}{ExpandCommand}{\param{const wxString \&}{command},\param{MessageParameters \&}{params}}
+
+This function is primarly intended for GetOpenCommand and GetPrintCommand
+usage but may be also used by the application directly if, for example, you want
+to use some non default command to open the file.
+
+The function replaces all occurences of
+\twocolwidtha{7cm}
+\begin{twocollist}\itemsep=0pt
+\twocolitem{format specificator}{with}
+\twocolitem{\%s}{the full file name}
+\twocolitem{\%t}{the MIME type}
+\twocolitem{\%\{param\}}{the value of the parameter {\it param}}
+\end{twocollist}
+using the MessageParameters object you pass to it.
+
+If there is no '\%s' in the command string (and the string is not empty), it is
+assumed that the command reads the data on stdin and so the effect is the same
+as "< \%s" were appended to the string.
+
+Unlike all other functions of this class, there is no error return for this
+function.
--- /dev/null
+/////////////////////////////////////////////////////////////////////////////
+// Name: common/mimetype.cpp
+// Purpose: classes and functions to manage MIME types
+// Author: Vadim Zeitlin
+// Modified by:
+// Created: 23.09.98
+// RCS-ID: $Id$
+// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
+// Licence: wxWindows license (part of wxExtra library)
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef __GNUG__
+ #pragma implementation "mimetype.h"
+#endif
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+// for compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+// wxWindows
+#ifndef WX_PRECOMP
+ #include "wx/string.h"
+#endif //WX_PRECOMP
+
+#include "wx/log.h"
+#include "wx/intl.h"
+#include "wx/dynarray.h"
+
+#ifdef __WXMSW__
+ #include "wx/msw/registry.h"
+#else // Unix
+ #include "wx/textfile.h"
+#endif // OS
+
+#include "wx/mimetype.h"
+
+// other standard headers
+#include <ctype.h>
+
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+// implementation classes, platform dependent
+#ifdef __WXMSW__
+
+// These classes use Windows registry to retrieve the required information.
+//
+// Keys used (not all of them are documented, so it might actually stop working
+// in futur versions of Windows...):
+// 1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME
+// types, each key has a string value "Extension" which gives (dot preceded)
+// extension for the files of this MIME type.
+//
+// 2. "HKCR\.ext" contains
+// a) unnamed value containing the "filetype"
+// b) value "Content Type" containing the MIME type
+//
+// 3. "HKCR\filetype" contains
+// a) unnamed value containing the description
+// b) subkey "DefaultIcon" with single unnamed value giving the icon index in
+// an icon file
+// c) shell\open\command and shell\open\print subkeys containing the commands
+// to open/print the file (the positional parameters are introduced by %1,
+// %2, ... in these strings, we change them to %s ourselves)
+
+class wxFileTypeImpl
+{
+public:
+ // ctor
+ wxFileTypeImpl() { }
+
+ // 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;
+};
+
+class wxMimeTypesManagerImpl
+{
+public:
+ // nothing to do here, we don't load any data but just go and fetch it from
+ // the registry when asked for
+ wxMimeTypesManagerImpl() { }
+
+ // implement containing class functions
+ wxFileType *GetFileTypeFromExtension(const wxString& ext);
+ wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
+
+ // this are NOPs under Windows
+ void ReadMailcap(const wxString& filename) { }
+ void ReadMimeTypes(const wxString& filename) { }
+};
+
+#else // Unix
+
+// this class uses both mailcap and mime.types to gather information about file
+// types.
+//
+// The information about mailcap file was extracted from metamail(1) sources and
+// documentation.
+//
+// Format of mailcap file: spaces are ignored, each line is either a comment
+// (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
+// A backslash can be used to quote semicolons and newlines (and, in fact,
+// anything else including itself).
+//
+// The first field is always the MIME type in the form of type/subtype (see RFC
+// 822) where subtype may be '*' meaning "any". Following metamail, we accept
+// "type" which means the same as "type/*", although I'm not sure whether this
+// is standard.
+//
+// The second field is always the command to run. It is subject to
+// parameter/filename expansion described below.
+//
+// All the following fields are optional and may not be present at all. If
+// they're present they may appear in any order, although each of them should
+// appear only once. The optional fields are the following:
+// * notes=xxx is an uninterpreted string which is silently ignored
+// * test=xxx is the command to be used to determine whether this mailcap line
+// applies to our data or not. The RHS of this field goes through the
+// parameter/filename expansion (as the 2nd field) and the resulting string
+// is executed. The line applies only if the command succeeds, i.e. returns 0
+// exit code.
+// * print=xxx is the command to be used to print (and not view) the data of
+// this type (parameter/filename expansion is done here too)
+// * edit=xxx is the command to open/edit the data of this type
+// * needsterminal means that a new console must be created for the viewer
+// * copiousoutput means that the viewer doesn't interact with the user but
+// produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
+// good example), thus it might be a good idea to use some kind of paging
+// mechanism.
+// * textualnewlines means not to perform CR/LF translation (not honored)
+// * 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:
+// * %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
+// replaced by an array of (content-type, temporary file name) pairs for all
+// message parts (TODO)
+// * %{parameter} is replaced with the value of parameter taken from
+// Content-type header line of the message.
+//
+// FIXME any docs with real descriptions of these files??
+//
+// There are 2 possible formats for mime.types file, one entry per line (used
+// for global mime.types) and "expanded" format where an entry takes multiple
+// lines (used for users mime.types).
+//
+// For both formats spaces are ignored and lines starting with a '#' are
+// comments. Each record has one of two following forms:
+// a) for "brief" format:
+// <mime type> <space separated list of extensions>
+// 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
+// with "type=" we assume the second format, otherwise the first one.
+
+// there may be more than one entry for one and the same mime type, to
+// choose the right one we have to run the command specified in the test
+// field on our data.
+class MailCapEntry
+{
+public:
+ // ctor
+ MailCapEntry(const wxString& openCmd,
+ const wxString& printCmd,
+ const wxString& testCmd)
+ : m_openCmd(openCmd), m_printCmd(printCmd), m_testCmd(testCmd)
+ {
+ m_next = NULL;
+ }
+
+ // accessors
+ const wxString& GetOpenCmd() const { return m_openCmd; }
+ const wxString& GetPrintCmd() const { return m_printCmd; }
+ const wxString& GetTestCmd() const { return m_testCmd; }
+
+ MailCapEntry *GetNext() const { return m_next; }
+
+ // operations
+ // prepend this element to the list
+ void Prepend(MailCapEntry *next) { m_next = next; }
+ // append to the list
+ void Append(MailCapEntry *next)
+ {
+ // FIXME slooow...
+ MailCapEntry *cur;
+ for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
+ ;
+
+ 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 );
+ }
+
+private:
+ wxString m_openCmd, // command to use to open/view the file
+ m_printCmd, // print
+ m_testCmd; // only apply this entry if test yields
+ // true (i.e. the command returns 0)
+
+ MailCapEntry *m_next; // in the linked list
+};
+
+WX_DEFINE_ARRAY(MailCapEntry *, ArrayTypeEntries);
+
+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...
+ 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);
+
+ // accessors
+ // get the string containing space separated extensions for the given
+ // file type
+ wxString GetExtension(size_t index) { return m_aExtensions[index]; }
+
+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
+};
+
+class wxFileTypeImpl
+{
+public:
+ // initialization functions
+ void Init(wxMimeTypesManagerImpl *manager, size_t index)
+ { m_manager = manager; m_index = index; }
+
+ // accessors
+ 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 GetDescription(wxString *desc) const
+ { *desc = m_manager->m_aDescriptions[m_index]; return TRUE; }
+
+ bool GetOpenCommand(wxString *openCmd,
+ const wxFileType::MessageParameters& params) const
+ {
+ return GetExpandedCommand(openCmd, params, TRUE);
+ }
+
+ bool GetPrintCommand(wxString *printCmd,
+ const wxFileType::MessageParameters& params) const
+ {
+ return GetExpandedCommand(printCmd, params, FALSE);
+ }
+
+private:
+ // get the entry which passes the test (may return NULL)
+ MailCapEntry *GetEntry(const wxFileType::MessageParameters& params) const;
+
+ // choose the correct entry to use and expand the command
+ bool GetExpandedCommand(wxString *expandedCmd,
+ const wxFileType::MessageParameters& params,
+ bool open) const;
+
+ wxMimeTypesManagerImpl *m_manager;
+ size_t m_index; // in the wxMimeTypesManagerImpl arrays
+};
+
+#endif // OS type
+
+// ============================================================================
+// implementation of the wrapper classes
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// wxFileType
+// ----------------------------------------------------------------------------
+
+wxString wxFileType::ExpandCommand(const wxString& command,
+ const wxFileType::MessageParameters& params)
+{
+ bool hasFilename = FALSE;
+
+ wxString str;
+ for ( const char *pc = command.c_str(); *pc != '\0'; pc++ ) {
+ if ( *pc == '%' ) {
+ switch ( *++pc ) {
+ case 's':
+ // '%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
+#if 0
+ if ( *(pc - 2) == '"' )
+ str << params.GetFileName();
+ else
+ str << '"' << params.GetFileName() << '"';
+#endif
+ str << params.GetFileName();
+ hasFilename = TRUE;
+ break;
+
+ case 't':
+ // '%t' expands into MIME type (quote it too just to be
+ // consistent)
+ str << '\'' << params.GetMimeType() << '\'';
+ break;
+
+ case '{':
+ {
+ const char *pEnd = strchr(pc, '}');
+ if ( pEnd == NULL ) {
+ wxString mimetype;
+ wxLogWarning(_("Unmatched '{' in an entry for "
+ "mime type %s."),
+ params.GetMimeType().c_str());
+ str << "%{";
+ }
+ else {
+ wxString param(pc + 1, pEnd - pc - 1);
+ str << '\'' << params.GetParamValue(param) << '\'';
+ pc = pEnd;
+ }
+ }
+ break;
+
+ case 'n':
+ case '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:
+ wxLogDebug("Unknown field %%%c in command '%s'.",
+ *pc, command.c_str());
+ str << *pc;
+ }
+ }
+ else {
+ str << *pc;
+ }
+ }
+
+ // 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() << '\'';
+ }
+
+ return str;
+}
+
+wxFileType::wxFileType()
+{
+ m_impl = new wxFileTypeImpl;
+}
+
+wxFileType::~wxFileType()
+{
+ delete m_impl;
+}
+
+bool wxFileType::GetExtensions(wxArrayString& extensions)
+{
+ return m_impl->GetExtensions(extensions);
+}
+
+bool wxFileType::GetMimeType(wxString *mimeType) const
+{
+ return m_impl->GetMimeType(mimeType);
+}
+
+bool wxFileType::GetIcon(wxIcon *icon) const
+{
+ return m_impl->GetIcon(icon);
+}
+
+bool wxFileType::GetDescription(wxString *desc) const
+{
+ return m_impl->GetDescription(desc);
+}
+
+bool
+wxFileType::GetOpenCommand(wxString *openCmd,
+ const wxFileType::MessageParameters& params) const
+{
+ return m_impl->GetOpenCommand(openCmd, params);
+}
+
+bool
+wxFileType::GetPrintCommand(wxString *printCmd,
+ const wxFileType::MessageParameters& params) const
+{
+ return m_impl->GetPrintCommand(printCmd, params);
+}
+
+// ----------------------------------------------------------------------------
+// wxMimeTypesManager
+// ----------------------------------------------------------------------------
+
+wxMimeTypesManager::wxMimeTypesManager()
+{
+ m_impl = new wxMimeTypesManagerImpl;
+}
+
+wxMimeTypesManager::~wxMimeTypesManager()
+{
+ delete m_impl;
+}
+
+wxFileType *
+wxMimeTypesManager::GetFileTypeFromExtension(const wxString& ext)
+{
+ return m_impl->GetFileTypeFromExtension(ext);
+}
+
+wxFileType *
+wxMimeTypesManager::GetFileTypeFromMimeType(const wxString& mimeType)
+{
+ return m_impl->GetFileTypeFromMimeType(mimeType);
+}
+
+// ============================================================================
+// real (OS specific) implementation
+// ============================================================================
+
+#ifdef __WXMSW__
+
+bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const
+{
+ // suppress possible error messages
+ wxLogNull nolog;
+ wxString strKey;
+ strKey << m_strFileType << "\\shell\\" << verb << "\\command";
+ wxRegKey key(wxRegKey::HKCR, strKey);
+
+ if ( key.Open() ) {
+ // it's the default value of the key
+ if ( key.QueryValue("", *command) ) {
+ // 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' ) {
+ // replace it with '%s'
+ command->SetChar(n + 1, 's');
+
+ return TRUE;
+ }
+ }
+
+ // we didn't find any '%1'!
+ // @@@ hack: append the filename at the end, hope that it will do
+ *command << " %s";
+
+ return TRUE;
+ }
+ }
+
+ // no such file type or no value
+ return FALSE;
+}
+
+// @@ this function is half implemented
+bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
+{
+ 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;
+ }
+ else {
+ extensions.Empty();
+ extensions.Add(m_ext);
+
+ // it's a lie too, we don't return _all_ extensions...
+ return TRUE;
+ }
+}
+
+bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
+{
+ // suppress possible error messages
+ wxLogNull nolog;
+ wxRegKey key(wxRegKey::HKCR, m_strFileType);
+ if ( key.Open() && key.QueryValue("Content Type", *mimeType) ) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
+{
+ wxString strIconKey;
+ strIconKey << m_strFileType << "\\DefaultIcon";
+
+ // suppress possible error messages
+ wxLogNull nolog;
+ wxRegKey key(wxRegKey::HKCR, strIconKey);
+
+ if ( key.Open() ) {
+ wxString strIcon;
+ // it's the default value of the key
+ if ( key.QueryValue("", 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 '%'
+ wxString strFullPath = strIcon.Before(','),
+ strIndex = strIcon.Right(',');
+
+ // index may be omitted, in which case Before(',') is empty and
+ // Right(',') is the whole string
+ if ( strFullPath.IsEmpty() ) {
+ strFullPath = strIndex;
+ strIndex = "0";
+ }
+
+ wxString strExpPath = wxExpandEnvVars(strFullPath);
+ int nIndex = atoi(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/...
+ wxLogDebug("incorrect registry entry '%s': no such icon.",
+ key.GetName().c_str());
+ break;
+
+ default:
+ icon->SetHICON((WXHICON)hIcon);
+ return TRUE;
+ }
+ }
+ }
+
+ // no such file type or no value or incorrect icon entry
+ return FALSE;
+}
+
+bool wxFileTypeImpl::GetDescription(wxString *desc) const
+{
+ // 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) ) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+// extension -> file type
+wxFileType *
+wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
+{
+ // add the leading point if necessary
+ wxString str;
+ if ( ext[0u] != '.' ) {
+ str = '.';
+ }
+ str << ext;
+
+ // suppress possible error messages
+ wxLogNull nolog;
+
+ wxString strFileType;
+ wxRegKey key(wxRegKey::HKCR, str);
+ if ( key.Open() ) {
+ // it's the default value of the key
+ if ( key.QueryValue("", strFileType) ) {
+ // create the new wxFileType object
+ wxFileType *fileType = new wxFileType;
+ fileType->m_impl->SetFileType(strFileType);
+ fileType->m_impl->SetExt(ext);
+
+ return fileType;
+ }
+ }
+
+ // unknown extension
+ return NULL;
+}
+
+// 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;
+ strKey << mimeType;
+
+ // suppress possible error messages
+ wxLogNull nolog;
+
+ wxString ext;
+ wxRegKey key(wxRegKey::HKCR, strKey);
+ if ( key.Open() ) {
+ if ( key.QueryValue("Extension", ext) ) {
+ return GetFileTypeFromExtension(ext);
+ }
+ }
+
+ // unknown MIME type
+ return NULL;
+}
+
+#else // 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 (@@ is it ok?)
+ command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
+
+ 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());
+ }
+
+ entry = entry->GetNext();
+ }
+
+ return entry;
+}
+
+bool
+wxFileTypeImpl::GetExpandedCommand(wxString *expandedCmd,
+ const wxFileType::MessageParameters& params,
+ bool open) const
+{
+ MailCapEntry *entry = GetEntry(params);
+ if ( entry == NULL ) {
+ // all tests failed...
+ return FALSE;
+ }
+
+ wxString cmd = open ? entry->GetOpenCmd() : entry->GetPrintCmd();
+ if ( cmd.IsEmpty() ) {
+ // may happen, especially for "print"
+ return FALSE;
+ }
+
+ *expandedCmd = wxFileType::ExpandCommand(cmd, params);
+ return TRUE;
+}
+
+bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
+{
+ wxString strExtensions = m_manager->GetExtension(m_index);
+ extensions.Empty();
+
+ // one extension in the space or comma delimitid list
+ wxString strExt;
+ for ( const char *p = strExtensions; ; p++ ) {
+ if ( *p == ' ' || *p == ',' || *p == '\0' ) {
+ if ( !strExt.IsEmpty() ) {
+ extensions.Add(strExt);
+ strExt.Empty();
+ }
+ //else: repeated spaces (shouldn't happen, but it's not that
+ // important if it does happen)
+
+ if ( *p == '\0' )
+ break;
+ }
+ else if ( *p == '.' ) {
+ // remove the dot from extension (but only if it's the first char)
+ if ( !strExt.IsEmpty() ) {
+ strExt += '.';
+ }
+ //else: no, don't append it
+ }
+ else {
+ strExt += *p;
+ }
+ }
+
+ return TRUE;
+}
+
+// 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[] =
+ {
+ "/etc",
+ "/usr/etc",
+ "/usr/local/etc",
+ "/etc/mail",
+ "/usr/public/lib"
+ };
+
+ // first read the system wide file(s)
+ for ( size_t n = 0; n < WXSIZEOF(aStandardLocations); n++ ) {
+ wxString dir = aStandardLocations[n];
+
+ wxString file = dir + "/mailcap";
+ if ( wxFile::Exists(file) ) {
+ ReadMailcap(file);
+ }
+
+ file = dir + "/mime.types";
+ if ( wxFile::Exists(file) ) {
+ ReadMimeTypes(file);
+ }
+ }
+
+ wxString strHome = getenv("HOME");
+
+ // and now the users mailcap
+ wxString strUserMailcap = strHome + "/.mailcap";
+ if ( wxFile::Exists(strUserMailcap) ) {
+ ReadMailcap(strUserMailcap);
+ }
+
+ // read the users mime.types
+ wxString strUserMimeTypes = strHome + "/.mime.types";
+ if ( wxFile::Exists(strUserMimeTypes) ) {
+ ReadMimeTypes(strUserMimeTypes);
+ }
+}
+
+wxFileType *
+wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
+{
+ wxFAIL_MSG("not implemented (must parse mime.types)");
+
+ return NULL;
+}
+
+wxFileType *
+wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
+{
+ // mime types are not case-sensitive
+ wxString mimetype(mimeType);
+ mimetype.MakeLower();
+
+ // first look for an exact match
+ int index = m_aTypes.Index(mimetype);
+ if ( index == NOT_FOUND ) {
+ // then try to find "text/*" as match for "text/plain" (for example)
+ // NB: if mimeType doesn't contain '/' at all, Left() will return the
+ // whole string - ok.
+ wxString strCategory = mimetype.Left('/');
+
+ size_t nCount = m_aTypes.Count();
+ for ( size_t n = 0; n < nCount; n++ ) {
+ if ( (m_aTypes[n].Before('/') == strCategory ) &&
+ m_aTypes[n].Right('/') == "*" ) {
+ index = n;
+ break;
+ }
+ }
+ }
+
+ if ( index != NOT_FOUND ) {
+ wxFileType *fileType = new wxFileType;
+ fileType->m_impl->Init(this, index);
+
+ return fileType;
+ }
+ else {
+ // not found...
+ return NULL;
+ }
+}
+
+void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
+{
+ wxLogTrace("--- Parsing mime.types file '%s' ---", strFileName.c_str());
+
+ wxTextFile file(strFileName);
+ if ( !file.Open() )
+ return;
+
+ // the information we extract
+ wxString strMimeType, strDesc, strExtensions;
+
+ 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();
+
+ // skip whitespace
+ while ( isspace(*pc) )
+ pc++;
+
+ // comment?
+ if ( *pc == '#' )
+ continue;
+
+ // detect file format
+ const char *pEqualSign = strchr(pc, '=');
+ if ( pEqualSign == NULL ) {
+ // brief format
+ // ------------
+
+ // first field is mime type
+ for ( strMimeType.Empty(); !isspace(*pc) && *pc != '\0'; pc++ ) {
+ strMimeType += *pc;
+ }
+
+ // skip whitespace
+ while ( isspace(*pc) )
+ pc++;
+
+ // take all the rest of the string
+ strExtensions = pc;
+
+ // no description...
+ strDesc.Empty();
+ }
+ else {
+ // expanded format
+ // ---------------
+
+ // the string on the left of '=' is the field name
+ wxString strLHS(pc, pEqualSign - pc);
+
+ // eat whitespace
+ for ( pc = pEqualSign + 1; isspace(*pc); pc++ )
+ ;
+
+ const char *pEnd;
+ if ( *pc == '"' ) {
+ // the string is quoted and ends at the matching quote
+ pEnd = strchr(++pc, '"');
+ if ( pEnd == NULL ) {
+ wxLogWarning(_("Mime.types file %s, line %d: unterminated "
+ "quoted string."),
+ strFileName.c_str(), nLine + 1);
+ }
+ }
+ else {
+ // unquoted stringends at the first space
+ for ( pEnd = pc; !isspace(*pEnd); pEnd++ )
+ ;
+ }
+
+ // 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 == '"' ) {
+ // skip this quote
+ pEnd++;
+ }
+
+ for ( pc = pEnd; isspace(*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 a trailing backslash entryEnded = FALSE
+
+ // now see what we got
+ if ( strLHS == "type" ) {
+ strMimeType = strRHS;
+ }
+ else if ( strLHS == "desc" ) {
+ strDesc = strRHS;
+ }
+ else if ( strLHS == "exts" ) {
+ strExtensions = strRHS;
+ }
+ else {
+ wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
+ strFileName.c_str(), nLine + 1, strLHS.c_str());
+ }
+
+ if ( !entryEnded ) {
+ // as we don't reset strMimeType, the next line in this entry
+ // will be interpreted correctly.
+ continue;
+ }
+ }
+
+ int index = m_aTypes.Index(strMimeType);
+ if ( index == NOT_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;
+ }
+ }
+
+ // check our data integriry
+ wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
+ m_aTypes.Count() == m_aExtensions.Count() &&
+ m_aTypes.Count() == m_aDescriptions.Count() );
+}
+
+void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
+{
+ wxLogTrace("--- Parsing mailcap file '%s' ---", strFileName.c_str());
+
+ wxTextFile file(strFileName);
+ if ( !file.Open() )
+ return;
+
+ // see the comments near the end of function for the reason we need this
+ wxArrayInt aEntryIndices;
+
+ 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();
+
+ // skip whitespace
+ while ( isspace(*pc) )
+ pc++;
+
+ // comment or empty string?
+ if ( *pc == '#' || *pc == '\0' )
+ continue;
+
+ // no, do parse
+
+ // what field are we currently in? The first 2 are fixed and there may
+ // be an arbitrary number of other fields -- currently, we are not
+ // interested in any of them, but we should parse them as well...
+ enum
+ {
+ Field_Type,
+ Field_OpenCmd,
+ Field_Other
+ } currentToken = Field_Type;
+
+ // the flags and field values on the current line
+ bool needsterminal = false,
+ copiousoutput = false;
+ wxString strType,
+ strOpenCmd,
+ strPrintCmd,
+ strTest,
+ strDesc,
+ curField; // accumulator
+ for ( bool cont = TRUE; cont; pc++ ) {
+ switch ( *pc ) {
+ case '\\':
+ // interpret the next character literally (notice that
+ // backslash can be used for line continuation)
+ if ( *++pc == '\0' ) {
+ // fetch the next line.
+
+ // pc currently points to nowhere, but after the next
+ // pc++ in the for line it will point to the beginning
+ // of the next line in the file
+ pc = file[++nLine].c_str() - 1;
+ }
+ else {
+ // just a normal character
+ curField += *pc;
+ }
+ break;
+
+ case '\0':
+ cont = FALSE; // end of line reached, exit the loop
+
+ // fall through
+
+ case ';':
+ // store this field and start looking for the next one
+
+ // trim whitespaces from both sides
+ curField.Trim(TRUE).Trim(FALSE);
+
+ switch ( currentToken ) {
+ case Field_Type:
+ strType = curField;
+ if ( strType.Find('/') == NOT_FOUND ) {
+ // we interpret "type" as "type/*"
+ strType += "/*";
+ }
+
+ currentToken = Field_OpenCmd;
+ break;
+
+ case Field_OpenCmd:
+ strOpenCmd = curField;
+
+ currentToken = Field_Other;
+ break;
+
+ case Field_Other:
+ {
+ // "good" mailcap entry?
+ bool ok = TRUE;
+
+ // is this something of the form foo=bar?
+ const char *pEq = strchr(curField, '=');
+ if ( pEq != NULL ) {
+ wxString lhs = curField.Left('='),
+ rhs = curField.After('=');
+
+ lhs.Trim(TRUE); // from right
+ rhs.Trim(FALSE); // from left
+
+ if ( lhs == "print" )
+ strPrintCmd = rhs;
+ else if ( lhs == "test" )
+ strTest = rhs;
+ else if ( lhs == "description" ) {
+ // it might be quoted
+ if ( rhs[0u] == '"' &&
+ rhs.Last() == '"' ) {
+ strDesc = wxString(rhs.c_str() + 1,
+ rhs.Len() - 2);
+ }
+ else {
+ strDesc = rhs;
+ }
+ }
+ else if ( lhs == "compose" ||
+ lhs == "composetyped" ||
+ lhs == "notes" ||
+ lhs == "edit" )
+ ; // ignore
+ else
+ ok = FALSE;
+
+ }
+ else {
+ // no, it's a simple flag
+ // TODO support the flags:
+ // 1. create an xterm for 'needsterminal'
+ // 2. append "| $PAGER" for 'copiousoutput'
+ if ( curField == "needsterminal" )
+ needsterminal = TRUE;
+ else if ( curField == "copiousoutput" )
+ copiousoutput = TRUE;
+ else if ( curField == "textualnewlines" )
+ ; // ignore
+ else
+ ok = FALSE;
+ }
+
+ 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()
+ );
+ }
+ }
+
+ // it already has this value
+ //currentToken = Field_Other;
+ break;
+
+ default:
+ wxFAIL_MSG("unknown field type in mailcap");
+ }
+
+ // next token starts immediately after ';'
+ curField.Empty();
+ break;
+
+ default:
+ curField += *pc;
+ }
+ }
+
+ // check that we really read something reasonable
+ if ( currentToken == Field_Type || currentToken == Field_OpenCmd ) {
+ wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
+ "ignored."),
+ strFileName.c_str(), nLine + 1);
+ }
+ else {
+ MailCapEntry *entry = new MailCapEntry(strOpenCmd,
+ strPrintCmd,
+ strTest);
+
+ strType.MakeLower();
+ int nIndex = m_aTypes.Index(strType);
+ if ( nIndex == NOT_FOUND ) {
+ // new file type
+ m_aTypes.Add(strType);
+
+ m_aEntries.Add(entry);
+ m_aExtensions.Add("");
+ 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) == NOT_FOUND ) {
+ // first time in this file
+ aEntryIndices.Add(nIndex);
+ entry->Prepend(m_aEntries[nIndex]);
+ m_aEntries[nIndex] = entry;
+ }
+ else {
+ // not the first time in _this_ file
+ entry->Append(m_aEntries[nIndex]);
+ }
+
+ if ( !strDesc.IsEmpty() ) {
+ // @@ replace the old one - what else can we do??
+ m_aDescriptions[nIndex] = strDesc;
+ }
+ }
+ }
+
+ // check our data integriry
+ wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
+ m_aTypes.Count() == m_aExtensions.Count() &&
+ m_aTypes.Count() == m_aDescriptions.Count() );
+ }
+}
+
+#endif // OS type
+
+/* vi: set cin tw=80 ts=4 sw=4: */
+