mimetype common implementation

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@55140 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Stefan Csomor 2008-08-21 07:30:33 +00:00
parent fb8dcb0528
commit 9a7fc42588

785
src/osx/core/mimetype.cpp Normal file
View File

@ -0,0 +1,785 @@
/////////////////////////////////////////////////////////////////////////////
// Name: src/osx/core/mimetype.cpp
// Purpose: classes and functions to manage MIME types
// Author: Vadim Zeitlin, Stefan Csomor
// Modified by:
// Created: 23.09.98
// RCS-ID: $Id: mimetype.cpp 54734 2008-07-21 01:33:51Z VZ $
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows licence (part of wxExtra library)
/////////////////////////////////////////////////////////////////////////////
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_MIMETYPE && wxUSE_FILE
#include "wx/unix/mimetype.h"
#ifndef WX_PRECOMP
#include "wx/dynarray.h"
#include "wx/string.h"
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/utils.h"
#endif
#include "wx/file.h"
#include "wx/confbase.h"
#include "wx/ffile.h"
#include "wx/dir.h"
#include "wx/tokenzr.h"
#include "wx/iconloc.h"
#include "wx/filename.h"
#include "wx/app.h"
#include "wx/apptrait.h"
// other standard headers
#include <ctype.h>
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// MIME code tracing mask
#define TRACE_MIME wxT("mime")
// ----------------------------------------------------------------------------
// wxFileTypeImpl (Unix)
// ----------------------------------------------------------------------------
wxString wxFileTypeImpl::GetExpandedCommand(const wxString & verb, const wxFileType::MessageParameters& params) const
{
wxString sTmp;
size_t i = 0;
while ( (i < m_index.GetCount() ) && sTmp.empty() )
{
sTmp = m_manager->GetCommand( verb, m_index[i] );
i++;
}
return wxFileType::ExpandCommand(sTmp, params);
}
bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
{
wxString sTmp;
size_t i = 0;
while ( (i < m_index.GetCount() ) && sTmp.empty() )
{
sTmp = m_manager->m_aIcons[m_index[i]];
i++;
}
if ( sTmp.empty() )
return false;
if ( iconLoc )
{
iconLoc->SetFileName(sTmp);
}
return true;
}
bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
{
mimeTypes.Clear();
size_t nCount = m_index.GetCount();
for (size_t i = 0; i < nCount; i++)
mimeTypes.Add(m_manager->m_aTypes[m_index[i]]);
return true;
}
size_t wxFileTypeImpl::GetAllCommands(wxArrayString *verbs,
wxArrayString *commands,
const wxFileType::MessageParameters& params) const
{
wxString vrb, cmd, sTmp;
size_t count = 0;
wxMimeTypeCommands * sPairs;
// verbs and commands have been cleared already in mimecmn.cpp...
// if we find no entries in the exact match, try the inexact match
for (size_t n = 0; ((count == 0) && (n < m_index.GetCount())); n++)
{
// list of verb = command pairs for this mimetype
sPairs = m_manager->m_aEntries [m_index[n]];
size_t i;
for ( i = 0; i < sPairs->GetCount(); i++ )
{
vrb = sPairs->GetVerb(i);
// some gnome entries have "." inside
vrb = vrb.AfterLast(wxT('.'));
cmd = sPairs->GetCmd(i);
if (! cmd.empty() )
{
cmd = wxFileType::ExpandCommand(cmd, params);
count++;
if ( vrb.IsSameAs(wxT("open")))
{
if ( verbs )
verbs->Insert(vrb, 0u);
if ( commands )
commands ->Insert(cmd, 0u);
}
else
{
if ( verbs )
verbs->Add(vrb);
if ( commands )
commands->Add(cmd);
}
}
}
}
return count;
}
bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
{
const wxString strExtensions = m_manager->GetExtension(m_index[0]);
extensions.Empty();
// one extension in the space or comma-delimited list
wxString strExt;
wxString::const_iterator end = strExtensions.end();
for ( wxString::const_iterator p = strExtensions.begin(); /* nothing */; ++p )
{
if ( p == end || *p == wxT(' ') || *p == wxT(',') )
{
if ( !strExt.empty() )
{
extensions.Add(strExt);
strExt.Empty();
}
//else: repeated spaces
// (shouldn't happen, but it's not that important if it does happen)
if ( p == end )
break;
}
else if ( *p == wxT('.') )
{
// remove the dot from extension (but only if it's the first char)
if ( !strExt.empty() )
{
strExt += wxT('.');
}
//else: no, don't append it
}
else
{
strExt += *p;
}
}
return true;
}
// set an arbitrary command:
// could adjust the code to ask confirmation if it already exists and
// overwriteprompt is true, but this is currently ignored as *Associate* has
// no overwrite prompt
bool
wxFileTypeImpl::SetCommand(const wxString& cmd,
const wxString& verb,
bool WXUNUSED(overwriteprompt))
{
wxArrayString strExtensions;
wxString strDesc, strIcon;
wxArrayString strTypes;
GetMimeTypes(strTypes);
if ( strTypes.IsEmpty() )
return false;
wxMimeTypeCommands *entry = new wxMimeTypeCommands();
entry->Add(verb + wxT("=") + cmd + wxT(" %s "));
bool ok = false;
size_t nCount = strTypes.GetCount();
for ( size_t i = 0; i < nCount; i++ )
{
if ( m_manager->DoAssociation
(
strTypes[i],
strIcon,
entry,
strExtensions,
strDesc
) )
{
// DoAssociation() took ownership of entry, don't delete it below
ok = true;
}
}
if ( !ok )
delete entry;
return ok;
}
// ignore index on the grounds that we only have one icon in a Unix file
bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int WXUNUSED(index))
{
if (strIcon.empty())
return false;
wxArrayString strExtensions;
wxString strDesc;
wxArrayString strTypes;
GetMimeTypes(strTypes);
if ( strTypes.IsEmpty() )
return false;
wxMimeTypeCommands *entry = new wxMimeTypeCommands();
bool ok = false;
size_t nCount = strTypes.GetCount();
for ( size_t i = 0; i < nCount; i++ )
{
if ( m_manager->DoAssociation
(
strTypes[i],
strIcon,
entry,
strExtensions,
strDesc
) )
{
// we don't need to free entry now, DoAssociation() took ownership
// of it
ok = true;
}
}
if ( !ok )
delete entry;
return ok;
}
// ----------------------------------------------------------------------------
// wxMimeTypesManagerImpl (Unix)
// ----------------------------------------------------------------------------
wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
{
m_initialized = false;
}
void wxMimeTypesManagerImpl::InitIfNeeded()
{
if ( !m_initialized )
{
// set the flag first to prevent recursion
m_initialized = true;
wxString wm = wxTheApp->GetTraits()->GetDesktopEnvironment();
if (wm == wxT("KDE"))
Initialize( wxMAILCAP_KDE );
else if (wm == wxT("GNOME"))
Initialize( wxMAILCAP_GNOME );
else
Initialize();
}
}
// read system and user mailcaps and other files
void wxMimeTypesManagerImpl::Initialize(int mailcapStyles,
const wxString& sExtraDir)
{
#ifdef __VMS
// XDG tables are never installed on OpenVMS
return;
#endif
#if 0
// Read MIME type - extension associations
LoadXDGGlobs( "/usr/share/mime/globs" );
LoadXDGGlobs( "/usr/local/share/mime/globs" );
// Load desktop files for XDG, and then override them with the defaults.
// We will override them one desktop file at a time, rather
// than one mime type at a time, but it should be a reasonable
// heuristic.
{
wxString xdgDataHome = wxGetenv("XDG_DATA_HOME");
if ( xdgDataHome.empty() )
xdgDataHome = wxGetHomeDir() + "/.local/share";
wxString xdgDataDirs = wxGetenv("XDG_DATA_DIRS");
if ( xdgDataDirs.empty() )
{
xdgDataDirs = "/usr/local/share:/usr/share";
if (mailcapStyles & wxMAILCAP_GNOME)
xdgDataDirs += ":/usr/share/gnome:/opt/gnome/share";
if (mailcapStyles & wxMAILCAP_KDE)
xdgDataDirs += ":/usr/share/kde3:/opt/kde3/share";
}
if ( !sExtraDir.empty() )
{
xdgDataDirs += ':';
xdgDataDirs += sExtraDir;
}
wxArrayString dirs;
wxStringTokenizer tokenizer(xdgDataDirs, ":");
while ( tokenizer.HasMoreTokens() )
{
wxString p = tokenizer.GetNextToken();
dirs.Add(p);
}
dirs.insert(dirs.begin(), xdgDataHome);
wxString defaultsList;
size_t i;
for (i = 0; i < dirs.GetCount(); i++)
{
wxString f = dirs[i];
if (f.Last() != '/') f += '/';
f += "applications/defaults.list";
if (wxFileExists(f))
{
defaultsList = f;
break;
}
}
// Load application files and associate them to corresponding mime types.
size_t nDirs = dirs.GetCount();
for (size_t nDir = 0; nDir < nDirs; nDir++)
{
wxString dirStr = dirs[nDir];
if (dirStr.Last() != '/') dirStr += '/';
dirStr += "applications";
LoadXDGAppsFilesFromDir(dirStr);
}
if (!defaultsList.IsEmpty())
{
wxArrayString deskTopFilesSeen;
wxMimeTextFile textfile(defaultsList);
if ( textfile.Open() )
{
int nIndex = textfile.pIndexOf( wxT("[Default Applications]") );
if (nIndex != wxNOT_FOUND)
{
for (i = nIndex+1; i < textfile.GetLineCount(); i++)
{
if (textfile.GetLine(i).Find(wxT("=")) != wxNOT_FOUND)
{
wxString mimeType = textfile.GetVerb(i);
wxString desktopFile = textfile.GetCmd(i);
if (deskTopFilesSeen.Index(desktopFile) == wxNOT_FOUND)
{
deskTopFilesSeen.Add(desktopFile);
size_t j;
for (j = 0; j < dirs.GetCount(); j++)
{
wxString desktopPath = dirs[j];
if (desktopPath.Last() != '/') desktopPath += '/';
desktopPath += "applications/";
desktopPath += desktopFile;
if (wxFileExists(desktopPath))
LoadXDGApp(desktopPath);
}
}
}
}
}
}
}
}
#endif
}
// clear data so you can read another group of WM files
void wxMimeTypesManagerImpl::ClearData()
{
m_aTypes.Clear();
m_aIcons.Clear();
m_aExtensions.Clear();
m_aDescriptions.Clear();
WX_CLEAR_ARRAY(m_aEntries);
m_aEntries.Empty();
}
wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl()
{
ClearData();
}
wxFileType * wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
{
InitIfNeeded();
wxString strType = ftInfo.GetMimeType();
wxString strDesc = ftInfo.GetDescription();
wxString strIcon = ftInfo.GetIconFile();
wxMimeTypeCommands *entry = new wxMimeTypeCommands();
if ( ! ftInfo.GetOpenCommand().empty())
entry->Add(wxT("open=") + ftInfo.GetOpenCommand() + wxT(" %s "));
if ( ! ftInfo.GetPrintCommand().empty())
entry->Add(wxT("print=") + ftInfo.GetPrintCommand() + wxT(" %s "));
// now find where these extensions are in the data store and remove them
wxArrayString sA_Exts = ftInfo.GetExtensions();
wxString sExt, sExtStore;
size_t i, nIndex;
size_t nExtCount = sA_Exts.GetCount();
for (i=0; i < nExtCount; i++)
{
sExt = sA_Exts.Item(i);
// clean up to just a space before and after
sExt.Trim().Trim(false);
sExt = wxT(' ') + sExt + wxT(' ');
size_t nCount = m_aExtensions.GetCount();
for (nIndex = 0; nIndex < nCount; nIndex++)
{
sExtStore = m_aExtensions.Item(nIndex);
if (sExtStore.Replace(sExt, wxT(" ") ) > 0)
m_aExtensions.Item(nIndex) = sExtStore;
}
}
if ( !DoAssociation(strType, strIcon, entry, sA_Exts, strDesc) )
return NULL;
return GetFileTypeFromMimeType(strType);
}
bool wxMimeTypesManagerImpl::DoAssociation(const wxString& strType,
const wxString& strIcon,
wxMimeTypeCommands *entry,
const wxArrayString& strExtensions,
const wxString& strDesc)
{
int nIndex = AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, true);
if ( nIndex == wxNOT_FOUND )
return false;
return true;
}
int wxMimeTypesManagerImpl::AddToMimeData(const wxString& strType,
const wxString& strIcon,
wxMimeTypeCommands *entry,
const wxArrayString& strExtensions,
const wxString& strDesc,
bool replaceExisting)
{
InitIfNeeded();
// ensure mimetype is always lower case
wxString mimeType = strType.Lower();
// is this a known MIME type?
int nIndex = m_aTypes.Index(mimeType);
if ( nIndex == wxNOT_FOUND )
{
// new file type
m_aTypes.Add(mimeType);
m_aIcons.Add(strIcon);
m_aEntries.Add(entry ? entry : new wxMimeTypeCommands);
// change nIndex so we can use it below to add the extensions
m_aExtensions.Add(wxEmptyString);
nIndex = m_aExtensions.size() - 1;
m_aDescriptions.Add(strDesc);
}
else // yes, we already have it
{
if ( replaceExisting )
{
// if new description change it
if ( !strDesc.empty())
m_aDescriptions[nIndex] = strDesc;
// if new icon change it
if ( !strIcon.empty())
m_aIcons[nIndex] = strIcon;
if ( entry )
{
delete m_aEntries[nIndex];
m_aEntries[nIndex] = entry;
}
}
else // add data we don't already have ...
{
// if new description add only if none
if ( m_aDescriptions[nIndex].empty() )
m_aDescriptions[nIndex] = strDesc;
// if new icon and no existing icon
if ( m_aIcons[nIndex].empty() )
m_aIcons[nIndex] = strIcon;
// add any new entries...
if ( entry )
{
wxMimeTypeCommands *entryOld = m_aEntries[nIndex];
size_t count = entry->GetCount();
for ( size_t i = 0; i < count; i++ )
{
const wxString& verb = entry->GetVerb(i);
if ( !entryOld->HasVerb(verb) )
{
entryOld->AddOrReplaceVerb(verb, entry->GetCmd(i));
}
}
// as we don't store it anywhere, it won't be deleted later as
// usual -- do it immediately instead
delete entry;
}
}
}
// always add the extensions to this mimetype
wxString& exts = m_aExtensions[nIndex];
// add all extensions we don't have yet
wxString ext;
size_t count = strExtensions.GetCount();
for ( size_t i = 0; i < count; i++ )
{
ext = strExtensions[i];
ext += wxT(' ');
if ( exts.Find(ext) == wxNOT_FOUND )
{
exts += ext;
}
}
// check data integrity
wxASSERT( m_aTypes.GetCount() == m_aEntries.GetCount() &&
m_aTypes.GetCount() == m_aExtensions.GetCount() &&
m_aTypes.GetCount() == m_aIcons.GetCount() &&
m_aTypes.GetCount() == m_aDescriptions.GetCount() );
return nIndex;
}
wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
{
if (ext.empty() )
return NULL;
InitIfNeeded();
size_t count = m_aExtensions.GetCount();
for ( size_t n = 0; n < count; n++ )
{
wxStringTokenizer tk(m_aExtensions[n], wxT(' '));
while ( tk.HasMoreTokens() )
{
// consider extensions as not being case-sensitive
if ( tk.GetNextToken().IsSameAs(ext, false /* no case */) )
{
// found
wxFileType *fileType = new wxFileType;
fileType->m_impl->Init(this, n);
return fileType;
}
}
}
return NULL;
}
wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
{
InitIfNeeded();
wxFileType * fileType = NULL;
// 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 != wxNOT_FOUND )
{
fileType = new wxFileType;
fileType->m_impl->Init(this, index);
}
// 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.
index = wxNOT_FOUND;
wxString strCategory = mimetype.BeforeFirst(wxT('/'));
size_t nCount = m_aTypes.GetCount();
for ( size_t n = 0; n < nCount; n++ )
{
if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") )
{
index = n;
break;
}
}
if ( index != wxNOT_FOUND )
{
// don't throw away fileType that was already found
if (!fileType)
fileType = new wxFileType;
fileType->m_impl->Init(this, index);
}
return fileType;
}
wxString wxMimeTypesManagerImpl::GetCommand(const wxString & verb, size_t nIndex) const
{
wxString command, testcmd, sV, sTmp;
sV = verb + wxT("=");
// list of verb = command pairs for this mimetype
wxMimeTypeCommands * sPairs = m_aEntries [nIndex];
size_t i;
size_t nCount = sPairs->GetCount();
for ( i = 0; i < nCount; i++ )
{
sTmp = sPairs->GetVerbCmd (i);
if ( sTmp.Contains(sV) )
command = sTmp.AfterFirst(wxT('='));
}
return command;
}
void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
{
InitIfNeeded();
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());
}
void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
const wxString& strExtensions,
const wxString& strDesc)
{
// reading mailcap may find image/* , while
// reading mime.types finds image/gif and no match is made
// this means all the get functions don't work fix this
wxString strIcon;
wxString sTmp = strExtensions;
wxArrayString sExts;
sTmp.Trim().Trim(false);
while (!sTmp.empty())
{
sExts.Add(sTmp.AfterLast(wxT(' ')));
sTmp = sTmp.BeforeLast(wxT(' '));
}
AddToMimeData(strMimeType, strIcon, NULL, sExts, strDesc, true);
}
size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
{
InitIfNeeded();
mimetypes.Empty();
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 '*')
const wxString &type = m_aTypes[n];
if ( type.Find(wxT('*')) == wxNOT_FOUND )
{
mimetypes.Add(type);
}
}
return mimetypes.GetCount();
}
// ----------------------------------------------------------------------------
// writing to MIME type files
// ----------------------------------------------------------------------------
bool wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
{
InitIfNeeded();
wxArrayString sMimeTypes;
ft->GetMimeTypes(sMimeTypes);
size_t i;
size_t nCount = sMimeTypes.GetCount();
for (i = 0; i < nCount; i ++)
{
const wxString &sMime = sMimeTypes.Item(i);
int nIndex = m_aTypes.Index(sMime);
if ( nIndex == wxNOT_FOUND)
{
// error if we get here ??
return false;
}
else
{
m_aTypes.RemoveAt(nIndex);
m_aEntries.RemoveAt(nIndex);
m_aExtensions.RemoveAt(nIndex);
m_aDescriptions.RemoveAt(nIndex);
m_aIcons.RemoveAt(nIndex);
}
}
// check data integrity
wxASSERT( m_aTypes.GetCount() == m_aEntries.GetCount() &&
m_aTypes.GetCount() == m_aExtensions.GetCount() &&
m_aTypes.GetCount() == m_aIcons.GetCount() &&
m_aTypes.GetCount() == m_aDescriptions.GetCount() );
return true;
}
#endif
// wxUSE_MIMETYPE && wxUSE_FILE