1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/mimetype.cpp
3 // Purpose: classes and functions to manage MIME types
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
12 // for compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
19 #if wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE
21 #include "wx/unix/mimetype.h"
24 #include "wx/dynarray.h"
25 #include "wx/string.h"
32 #include "wx/confbase.h"
35 #include "wx/textfile.h"
37 #include "wx/tokenzr.h"
38 #include "wx/iconloc.h"
39 #include "wx/filename.h"
41 #include "wx/apptrait.h"
43 // other standard headers
53 wxMimeTextFile(const wxString
& fname
)
58 void AddLine( const wxString
&line
) { }
60 bool Open( const wxString
&fname
)
63 wxFFile
file( fname
);
66 size_t size
= file
.Length();
67 wxCharBuffer
buffer( size
);
68 file
.Read( (void*) (const char*) buffer
, size
);
69 wxString all
= wxString::FromUTF8( buffer
, size
);
71 wxStringTokenizer
tok( all
, "\n" );
72 while (tok
.HasMoreTokens())
74 wxString t
= tok
.GetNextToken();
76 if ((!!t
) && (t
.Find( "comment" ) != 0) && (t
.Find( "generic" ) != 0))
81 bool Open() { return Open(m_fname
); }
82 bool Create( const wxString
&fname
) { return false; }
83 bool Write() { return false; }
84 bool Close() { return false; }
86 unsigned int GetLineCount() const { return m_text
.GetCount(); }
87 wxString
&GetLine( unsigned int line
) { return m_text
[line
]; }
89 int pIndexOf(const wxString
& sSearch
,
90 bool bIncludeComments
= false,
93 wxString sTest
= sSearch
;
95 for(size_t i
= iStart
; i
< GetLineCount(); i
++)
97 wxString sLine
= GetLine(i
);
98 if(bIncludeComments
|| ! sLine
.StartsWith(wxT("#")))
100 if(sLine
.StartsWith(sTest
))
107 bool CommentLine(int nIndex
)
111 if (nIndex
>= (int)GetLineCount() )
114 GetLine(nIndex
) = GetLine(nIndex
).Prepend(wxT("#"));
118 bool CommentLine(const wxString
& sTest
)
120 int nIndex
= pIndexOf(sTest
);
123 if (nIndex
>= (int)GetLineCount() )
126 GetLine(nIndex
) = GetLine(nIndex
).Prepend(wxT("#"));
130 wxString
GetVerb(size_t i
)
132 if (i
> GetLineCount() )
133 return wxEmptyString
;
135 wxString sTmp
= GetLine(i
).BeforeFirst(wxT('='));
139 wxString
GetCmd(size_t i
)
141 if (i
> GetLineCount() )
142 return wxEmptyString
;
144 wxString sTmp
= GetLine(i
).AfterFirst(wxT('='));
149 wxArrayString m_text
;
153 // ----------------------------------------------------------------------------
155 // ----------------------------------------------------------------------------
157 // MIME code tracing mask
158 #define TRACE_MIME wxT("mime")
160 // give trace messages about the results of mailcap tests
161 #define TRACE_MIME_TEST wxT("mimetest")
163 // ----------------------------------------------------------------------------
165 // ----------------------------------------------------------------------------
167 // ----------------------------------------------------------------------------
170 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype
171 // may be found in either of the following locations
173 // 1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
174 // 2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
176 // The format of a .kdelnk file is almost the same as the one used by
177 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the
178 // value for the entry "Type"
180 // kde writing; see http://webcvs.kde.org/cgi-bin/cvsweb.cgi/~checkout~/kdelibs/kio/DESKTOP_ENTRY_STANDARD
181 // for now write to .kdelnk but should eventually do .desktop instead (in preference??)
183 bool wxMimeTypesManagerImpl::CheckKDEDirsExist( const wxString
&sOK
, const wxString
&sTest
)
187 return wxDir::Exists(sOK
);
191 wxString sStart
= sOK
+ wxT("/") + sTest
.BeforeFirst(wxT('/'));
192 if (!wxDir::Exists(sStart
))
194 wxString sEnd
= sTest
.AfterFirst(wxT('/'));
195 return CheckKDEDirsExist(sStart
, sEnd
);
199 bool wxMimeTypesManagerImpl::WriteKDEMimeFile(int index
, bool delete_index
)
201 wxMimeTextFile appoutfile
, mimeoutfile
;
202 wxString sHome
= wxGetHomeDir();
203 wxString sTmp
= wxT(".kde/share/mimelnk/");
204 wxString sMime
= m_aTypes
[index
];
205 CheckKDEDirsExist(sHome
, sTmp
+ sMime
.BeforeFirst(wxT('/')) );
206 sTmp
= sHome
+ wxT('/') + sTmp
+ sMime
+ wxT(".kdelnk");
209 bool bMimeExists
= mimeoutfile
.Open(sTmp
);
212 bTemp
= mimeoutfile
.Create(sTmp
);
213 // some unknown error eg out of disk space
218 sTmp
= wxT(".kde/share/applnk/");
219 CheckKDEDirsExist(sHome
, sTmp
+ sMime
.AfterFirst(wxT('/')) );
220 sTmp
= sHome
+ wxT('/') + sTmp
+ sMime
.AfterFirst(wxT('/')) + wxT(".kdelnk");
223 bAppExists
= appoutfile
.Open(sTmp
);
226 bTemp
= appoutfile
.Create(sTmp
);
227 // some unknown error eg out of disk space
232 // fixed data; write if new file
235 mimeoutfile
.AddLine(wxT("#KDE Config File"));
236 mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]"));
237 mimeoutfile
.AddLine(wxT("Version=1.0"));
238 mimeoutfile
.AddLine(wxT("Type=MimeType"));
239 mimeoutfile
.AddLine(wxT("MimeType=") + sMime
);
244 mimeoutfile
.AddLine(wxT("#KDE Config File"));
245 mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]"));
246 appoutfile
.AddLine(wxT("Version=1.0"));
247 appoutfile
.AddLine(wxT("Type=Application"));
248 appoutfile
.AddLine(wxT("MimeType=") + sMime
+ wxT(';'));
253 mimeoutfile
.CommentLine(wxT("Comment="));
255 mimeoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]);
256 appoutfile
.CommentLine(wxT("Name="));
258 appoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]);
260 sTmp
= m_aIcons
[index
];
261 // we can either give the full path, or the shortfilename if its in
262 // one of the directories we search
263 mimeoutfile
.CommentLine(wxT("Icon=") );
265 mimeoutfile
.AddLine(wxT("Icon=") + sTmp
);
266 appoutfile
.CommentLine(wxT("Icon=") );
268 appoutfile
.AddLine(wxT("Icon=") + sTmp
);
270 sTmp
= wxT(" ") + m_aExtensions
[index
];
272 wxStringTokenizer
tokenizer(sTmp
, wxT(" "));
273 sTmp
= wxT("Patterns=");
274 mimeoutfile
.CommentLine(sTmp
);
275 while ( tokenizer
.HasMoreTokens() )
277 // holds an extension; need to change it to *.ext;
278 wxString e
= wxT("*.") + tokenizer
.GetNextToken() + wxT(";");
283 mimeoutfile
.AddLine(sTmp
);
285 wxMimeTypeCommands
* entries
= m_aEntries
[index
];
286 // if we don't find open just have an empty string ... FIX this
287 sTmp
= entries
->GetCommandForVerb(wxT("open"));
288 sTmp
.Replace( wxT("%s"), wxT("%f") );
290 mimeoutfile
.CommentLine(wxT("DefaultApp=") );
292 mimeoutfile
.AddLine(wxT("DefaultApp=") + sTmp
);
294 sTmp
.Replace( wxT("%f"), wxT("") );
295 appoutfile
.CommentLine(wxT("Exec="));
297 appoutfile
.AddLine(wxT("Exec=") + sTmp
);
299 if (entries
->GetCount() > 1)
301 //other actions as well as open
305 if (mimeoutfile
.Write())
308 if (appoutfile
.Write())
315 // Read a KDE .desktop file of type 'Application'
316 void wxMimeTypesManagerImpl::LoadKDEApp(const wxString
& filename
)
318 wxLogTrace(TRACE_MIME
, wxT("loading KDE file %s"), filename
.c_str());
321 if ( !file
.Open(filename
) )
324 // Here, only type 'Application' should be considered.
325 int nIndex
= file
.pIndexOf( "Type=" );
326 if (nIndex
!= wxNOT_FOUND
&& file
.GetCmd(nIndex
) != "application")
329 // The hidden entry specifies a file to be ignored.
330 nIndex
= file
.pIndexOf( "Hidden=" );
331 if (nIndex
!= wxNOT_FOUND
&& file
.GetCmd(nIndex
) == "true")
334 // Semicolon separated list of mime types handled by the application.
335 nIndex
= file
.pIndexOf( wxT("MimeType=") );
336 if (nIndex
== wxNOT_FOUND
)
338 wxString mimetypes
= file
.GetCmd (nIndex
);
340 // Name of the application
342 nIndex
= wxNOT_FOUND
;
343 #if wxUSE_INTL // try "Name[locale name]" first
344 wxLocale
*locale
= wxGetLocale();
346 nIndex
= file
.pIndexOf(_T("Name[")+locale
->GetName()+_T("]="));
348 if(nIndex
== wxNOT_FOUND
)
349 nIndex
= file
.pIndexOf( wxT("Name=") );
350 if(nIndex
!= wxNOT_FOUND
)
351 nameapp
= file
.GetCmd(nIndex
);
353 // Icon of the application.
354 wxString nameicon
, namemini
;
355 nIndex
= wxNOT_FOUND
;
356 #if wxUSE_INTL // try "Icon[locale name]" first
358 nIndex
= file
.pIndexOf(_T("Icon[")+locale
->GetName()+_T("]="));
360 if(nIndex
== wxNOT_FOUND
)
361 nIndex
= file
.pIndexOf( wxT("Icon=") );
362 if(nIndex
!= wxNOT_FOUND
) {
363 nameicon
= wxString(wxT("--icon ")) + file
.GetCmd(nIndex
);
364 namemini
= wxString(wxT("--miniicon ")) + file
.GetCmd(nIndex
);
367 // Replace some of the field code in the 'Exec' entry.
368 // TODO: deal with %d, %D, %n, %N, %k and %v (but last one is deprecated)
369 nIndex
= file
.pIndexOf( wxT("Exec=") );
370 if (nIndex
== wxNOT_FOUND
)
372 wxString sCmd
= file
.GetCmd(nIndex
);
373 // we expect %f; others including %F and %U and %u are possible
374 sCmd
.Replace(wxT("%F"), wxT("%f"));
375 sCmd
.Replace(wxT("%U"), wxT("%f"));
376 sCmd
.Replace(wxT("%u"), wxT("%f"));
377 if (0 == sCmd
.Replace ( wxT("%f"), wxT("%s") ))
378 sCmd
= sCmd
+ wxT(" %s");
379 sCmd
.Replace(wxT("%c"), nameapp
);
380 sCmd
.Replace(wxT("%i"), nameicon
);
381 sCmd
.Replace(wxT("%m"), namemini
);
383 wxStringTokenizer
tokenizer(mimetypes
, _T(";"));
384 while(tokenizer
.HasMoreTokens()) {
385 wxString mimetype
= tokenizer
.GetNextToken().Lower();
386 nIndex
= m_aTypes
.Index(mimetype
);
387 if(nIndex
!= wxNOT_FOUND
) { // is this a known MIME type?
388 wxMimeTypeCommands
* entry
= m_aEntries
[nIndex
];
389 entry
->AddOrReplaceVerb(wxT("open"), sCmd
);
394 void wxMimeTypesManagerImpl::LoadKDEAppsFilesFromDir(const wxString
& dirname
)
396 // Don't complain if we don't have permissions to read - it confuses users
399 if(! wxDir::Exists(dirname
))
402 if ( !dir
.IsOpened() )
406 // Look into .desktop files
407 bool cont
= dir
.GetFirst(&filename
, _T("*.desktop"), wxDIR_FILES
);
410 wxFileName
p(dirname
, filename
);
411 LoadKDEApp( p
.GetFullPath() );
412 cont
= dir
.GetNext(&filename
);
417 // Look recursively into subdirs
418 cont
= dir
.GetFirst(&filename
, wxEmptyString
, wxDIR_DIRS
);
421 wxFileName
p(dirname
, wxEmptyString
);
422 p
.AppendDir(filename
);
423 LoadKDEAppsFilesFromDir( p
.GetPath() );
424 cont
= dir
.GetNext(&filename
);
429 // ----------------------------------------------------------------------------
430 // wxFileTypeImpl (Unix)
431 // ----------------------------------------------------------------------------
433 wxString
wxFileTypeImpl::GetExpandedCommand(const wxString
& verb
, const wxFileType::MessageParameters
& params
) const
437 while ( (i
< m_index
.GetCount() ) && sTmp
.empty() )
439 sTmp
= m_manager
->GetCommand( verb
, m_index
[i
] );
443 return wxFileType::ExpandCommand(sTmp
, params
);
446 bool wxFileTypeImpl::GetIcon(wxIconLocation
*iconLoc
) const
450 while ( (i
< m_index
.GetCount() ) && sTmp
.empty() )
452 sTmp
= m_manager
->m_aIcons
[m_index
[i
]];
461 iconLoc
->SetFileName(sTmp
);
467 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
470 size_t nCount
= m_index
.GetCount();
471 for (size_t i
= 0; i
< nCount
; i
++)
472 mimeTypes
.Add(m_manager
->m_aTypes
[m_index
[i
]]);
477 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
*verbs
,
478 wxArrayString
*commands
,
479 const wxFileType::MessageParameters
& params
) const
481 wxString vrb
, cmd
, sTmp
;
483 wxMimeTypeCommands
* sPairs
;
485 // verbs and commands have been cleared already in mimecmn.cpp...
486 // if we find no entries in the exact match, try the inexact match
487 for (size_t n
= 0; ((count
== 0) && (n
< m_index
.GetCount())); n
++)
489 // list of verb = command pairs for this mimetype
490 sPairs
= m_manager
->m_aEntries
[m_index
[n
]];
492 for ( i
= 0; i
< sPairs
->GetCount(); i
++ )
494 vrb
= sPairs
->GetVerb(i
);
495 // some gnome entries have "." inside
496 vrb
= vrb
.AfterLast(wxT('.'));
497 cmd
= sPairs
->GetCmd(i
);
500 cmd
= wxFileType::ExpandCommand(cmd
, params
);
502 if ( vrb
.IsSameAs(wxT("open")))
505 verbs
->Insert(vrb
, 0u);
507 commands
->Insert(cmd
, 0u);
523 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
525 const wxString strExtensions
= m_manager
->GetExtension(m_index
[0]);
528 // one extension in the space or comma-delimited list
530 wxString::const_iterator end
= strExtensions
.end();
531 for ( wxString::const_iterator p
= strExtensions
.begin(); /* nothing */; ++p
)
533 if ( p
== end
|| *p
== wxT(' ') || *p
== wxT(',') )
535 if ( !strExt
.empty() )
537 extensions
.Add(strExt
);
540 //else: repeated spaces
541 // (shouldn't happen, but it's not that important if it does happen)
546 else if ( *p
== wxT('.') )
548 // remove the dot from extension (but only if it's the first char)
549 if ( !strExt
.empty() )
553 //else: no, don't append it
564 // set an arbitrary command:
565 // could adjust the code to ask confirmation if it already exists and
566 // overwriteprompt is true, but this is currently ignored as *Associate* has
567 // no overwrite prompt
569 wxFileTypeImpl::SetCommand(const wxString
& cmd
,
570 const wxString
& verb
,
571 bool WXUNUSED(overwriteprompt
))
573 wxArrayString strExtensions
;
574 wxString strDesc
, strIcon
;
576 wxArrayString strTypes
;
577 GetMimeTypes(strTypes
);
578 if ( strTypes
.IsEmpty() )
581 wxMimeTypeCommands
*entry
= new wxMimeTypeCommands();
582 entry
->Add(verb
+ wxT("=") + cmd
+ wxT(" %s "));
585 size_t nCount
= strTypes
.GetCount();
586 for ( size_t i
= 0; i
< nCount
; i
++ )
588 if ( m_manager
->DoAssociation
597 // DoAssociation() took ownership of entry, don't delete it below
608 // ignore index on the grounds that we only have one icon in a Unix file
609 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon
, int WXUNUSED(index
))
614 wxArrayString strExtensions
;
617 wxArrayString strTypes
;
618 GetMimeTypes(strTypes
);
619 if ( strTypes
.IsEmpty() )
622 wxMimeTypeCommands
*entry
= new wxMimeTypeCommands();
624 size_t nCount
= strTypes
.GetCount();
625 for ( size_t i
= 0; i
< nCount
; i
++ )
627 if ( m_manager
->DoAssociation
636 // we don't need to free entry now, DoAssociation() took ownership
648 // ----------------------------------------------------------------------------
649 // wxMimeTypesManagerImpl (Unix)
650 // ----------------------------------------------------------------------------
652 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
654 m_initialized
= false;
657 void wxMimeTypesManagerImpl::InitIfNeeded()
659 if ( !m_initialized
)
661 // set the flag first to prevent recursion
662 m_initialized
= true;
664 wxString wm
= wxTheApp
->GetTraits()->GetDesktopEnvironment();
666 if (wm
== wxT("KDE"))
667 Initialize( wxMAILCAP_KDE
);
668 else if (wm
== wxT("GNOME"))
669 Initialize( wxMAILCAP_GNOME
);
675 // read system and user mailcaps and other files
676 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
,
677 const wxString
& sExtraDir
)
680 // XDG tables are never installed on OpenVMS
684 // Load desktop files for Gnome, and then override them with the Gnome defaults.
685 // We will override them one desktop file at a time, rather
686 // than one mime type at a time, but it should be a reasonable
689 wxString xdgDataHome
= wxGetenv(wxT("XDG_DATA_HOME"));
690 if ( xdgDataHome
.empty() )
691 xdgDataHome
= wxGetHomeDir() + wxT("/.local/share");
692 wxString xdgDataDirs
= wxGetenv(wxT("XDG_DATA_DIRS"));
693 if ( xdgDataDirs
.empty() )
694 xdgDataDirs
= wxT("/usr/local/share:/usr/share:/usr/share/gnome");
697 wxStringTokenizer
tokenizer(xdgDataDirs
, wxT(":"));
698 while ( tokenizer
.HasMoreTokens() )
700 wxString p
= tokenizer
.GetNextToken();
703 dirs
.insert(dirs
.begin(), xdgDataHome
);
705 wxString defaultsList
;
707 for (i
= 0; i
< dirs
.GetCount(); i
++)
709 wxString f
= dirs
[i
];
710 if (f
.Last() != '/') f
+= '/';
711 f
+= "applications/defaults.list";
719 // Load application files and associate them to corresponding mime types.
720 size_t nDirs
= dirs
.GetCount();
721 for (size_t nDir
= 0; nDir
< nDirs
; nDir
++)
723 wxString dirStr
= dirs
[nDir
];
724 if (dirStr
.Last() != '/') dirStr
+= '/';
725 dirStr
+= "applications";
726 LoadKDEAppsFilesFromDir(dirStr
);
729 if (!defaultsList
.IsEmpty())
731 wxArrayString deskTopFilesSeen
;
733 wxMimeTextFile
textfile(defaultsList
);
734 if ( textfile
.Open() )
736 int nIndex
= textfile
.pIndexOf( wxT("[Default Applications]") );
737 if (nIndex
!= wxNOT_FOUND
)
739 for (i
= nIndex
+1; i
< textfile
.GetLineCount(); i
++)
741 if (textfile
.GetLine(i
).Find(wxT("=")) != wxNOT_FOUND
)
743 wxString mimeType
= textfile
.GetVerb(i
);
744 wxString desktopFile
= textfile
.GetCmd(i
);
746 if (deskTopFilesSeen
.Index(desktopFile
) == wxNOT_FOUND
)
748 deskTopFilesSeen
.Add(desktopFile
);
750 for (j
= 0; j
< dirs
.GetCount(); j
++)
752 wxString desktopPath
= dirs
[j
];
753 if (desktopPath
.Last() == '/')
754 desktopPath
+= "applications/";
756 desktopPath
+= "/applications/";
757 desktopPath
+= desktopFile
;
759 if (wxFileExists(desktopPath
))
760 LoadKDEApp(desktopPath
);
771 // clear data so you can read another group of WM files
772 void wxMimeTypesManagerImpl::ClearData()
776 m_aExtensions
.Clear();
777 m_aDescriptions
.Clear();
779 WX_CLEAR_ARRAY(m_aEntries
);
783 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl()
788 wxFileType
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
792 wxString strType
= ftInfo
.GetMimeType();
793 wxString strDesc
= ftInfo
.GetDescription();
794 wxString strIcon
= ftInfo
.GetIconFile();
796 wxMimeTypeCommands
*entry
= new wxMimeTypeCommands();
798 if ( ! ftInfo
.GetOpenCommand().empty())
799 entry
->Add(wxT("open=") + ftInfo
.GetOpenCommand() + wxT(" %s "));
800 if ( ! ftInfo
.GetPrintCommand().empty())
801 entry
->Add(wxT("print=") + ftInfo
.GetPrintCommand() + wxT(" %s "));
803 // now find where these extensions are in the data store and remove them
804 wxArrayString sA_Exts
= ftInfo
.GetExtensions();
805 wxString sExt
, sExtStore
;
807 size_t nExtCount
= sA_Exts
.GetCount();
808 for (i
=0; i
< nExtCount
; i
++)
810 sExt
= sA_Exts
.Item(i
);
812 // clean up to just a space before and after
813 sExt
.Trim().Trim(false);
814 sExt
= wxT(' ') + sExt
+ wxT(' ');
815 size_t nCount
= m_aExtensions
.GetCount();
816 for (nIndex
= 0; nIndex
< nCount
; nIndex
++)
818 sExtStore
= m_aExtensions
.Item(nIndex
);
819 if (sExtStore
.Replace(sExt
, wxT(" ") ) > 0)
820 m_aExtensions
.Item(nIndex
) = sExtStore
;
824 if ( !DoAssociation(strType
, strIcon
, entry
, sA_Exts
, strDesc
) )
827 return GetFileTypeFromMimeType(strType
);
830 bool wxMimeTypesManagerImpl::DoAssociation(const wxString
& strType
,
831 const wxString
& strIcon
,
832 wxMimeTypeCommands
*entry
,
833 const wxArrayString
& strExtensions
,
834 const wxString
& strDesc
)
836 int nIndex
= AddToMimeData(strType
, strIcon
, entry
, strExtensions
, strDesc
, true);
838 if ( nIndex
== wxNOT_FOUND
)
841 return WriteMimeInfo(nIndex
, false);
844 bool wxMimeTypesManagerImpl::WriteMimeInfo(int nIndex
, bool delete_mime
)
848 // Don't write GNOME files here as this is not
849 // allowed and simply doesn't work
851 // if (m_mailcapStylesInited & wxMAILCAP_KDE)
853 // write in KDE format;
854 if (WriteKDEMimeFile(nIndex
, delete_mime
) )
861 int wxMimeTypesManagerImpl::AddToMimeData(const wxString
& strType
,
862 const wxString
& strIcon
,
863 wxMimeTypeCommands
*entry
,
864 const wxArrayString
& strExtensions
,
865 const wxString
& strDesc
,
866 bool replaceExisting
)
870 // ensure mimetype is always lower case
871 wxString mimeType
= strType
.Lower();
873 // is this a known MIME type?
874 int nIndex
= m_aTypes
.Index(mimeType
);
875 if ( nIndex
== wxNOT_FOUND
)
878 m_aTypes
.Add(mimeType
);
879 m_aIcons
.Add(strIcon
);
880 m_aEntries
.Add(entry
? entry
: new wxMimeTypeCommands
);
882 // change nIndex so we can use it below to add the extensions
883 m_aExtensions
.Add(wxEmptyString
);
884 nIndex
= m_aExtensions
.size() - 1;
886 m_aDescriptions
.Add(strDesc
);
888 else // yes, we already have it
890 if ( replaceExisting
)
892 // if new description change it
893 if ( !strDesc
.empty())
894 m_aDescriptions
[nIndex
] = strDesc
;
896 // if new icon change it
897 if ( !strIcon
.empty())
898 m_aIcons
[nIndex
] = strIcon
;
902 delete m_aEntries
[nIndex
];
903 m_aEntries
[nIndex
] = entry
;
906 else // add data we don't already have ...
908 // if new description add only if none
909 if ( m_aDescriptions
[nIndex
].empty() )
910 m_aDescriptions
[nIndex
] = strDesc
;
912 // if new icon and no existing icon
913 if ( m_aIcons
[nIndex
].empty() )
914 m_aIcons
[nIndex
] = strIcon
;
916 // add any new entries...
919 wxMimeTypeCommands
*entryOld
= m_aEntries
[nIndex
];
921 size_t count
= entry
->GetCount();
922 for ( size_t i
= 0; i
< count
; i
++ )
924 const wxString
& verb
= entry
->GetVerb(i
);
925 if ( !entryOld
->HasVerb(verb
) )
927 entryOld
->AddOrReplaceVerb(verb
, entry
->GetCmd(i
));
931 // as we don't store it anywhere, it won't be deleted later as
932 // usual -- do it immediately instead
938 // always add the extensions to this mimetype
939 wxString
& exts
= m_aExtensions
[nIndex
];
941 // add all extensions we don't have yet
943 size_t count
= strExtensions
.GetCount();
944 for ( size_t i
= 0; i
< count
; i
++ )
946 ext
= strExtensions
[i
];
949 if ( exts
.Find(ext
) == wxNOT_FOUND
)
955 // check data integrity
956 wxASSERT( m_aTypes
.GetCount() == m_aEntries
.GetCount() &&
957 m_aTypes
.GetCount() == m_aExtensions
.GetCount() &&
958 m_aTypes
.GetCount() == m_aIcons
.GetCount() &&
959 m_aTypes
.GetCount() == m_aDescriptions
.GetCount() );
964 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
)
971 size_t count
= m_aExtensions
.GetCount();
972 for ( size_t n
= 0; n
< count
; n
++ )
974 wxStringTokenizer
tk(m_aExtensions
[n
], wxT(' '));
976 while ( tk
.HasMoreTokens() )
978 // consider extensions as not being case-sensitive
979 if ( tk
.GetNextToken().IsSameAs(ext
, false /* no case */) )
982 wxFileType
*fileType
= new wxFileType
;
983 fileType
->m_impl
->Init(this, n
);
993 wxFileType
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
997 wxFileType
* fileType
= NULL
;
998 // mime types are not case-sensitive
999 wxString
mimetype(mimeType
);
1000 mimetype
.MakeLower();
1002 // first look for an exact match
1003 int index
= m_aTypes
.Index(mimetype
);
1004 if ( index
!= wxNOT_FOUND
)
1006 fileType
= new wxFileType
;
1007 fileType
->m_impl
->Init(this, index
);
1010 // then try to find "text/*" as match for "text/plain" (for example)
1011 // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
1012 // the whole string - ok.
1014 index
= wxNOT_FOUND
;
1015 wxString strCategory
= mimetype
.BeforeFirst(wxT('/'));
1017 size_t nCount
= m_aTypes
.GetCount();
1018 for ( size_t n
= 0; n
< nCount
; n
++ )
1020 if ( (m_aTypes
[n
].BeforeFirst(wxT('/')) == strCategory
) &&
1021 m_aTypes
[n
].AfterFirst(wxT('/')) == wxT("*") )
1028 if ( index
!= wxNOT_FOUND
)
1030 // don't throw away fileType that was already found
1032 fileType
= new wxFileType
;
1033 fileType
->m_impl
->Init(this, index
);
1039 wxString
wxMimeTypesManagerImpl::GetCommand(const wxString
& verb
, size_t nIndex
) const
1041 wxString command
, testcmd
, sV
, sTmp
;
1042 sV
= verb
+ wxT("=");
1044 // list of verb = command pairs for this mimetype
1045 wxMimeTypeCommands
* sPairs
= m_aEntries
[nIndex
];
1048 size_t nCount
= sPairs
->GetCount();
1049 for ( i
= 0; i
< nCount
; i
++ )
1051 sTmp
= sPairs
->GetVerbCmd (i
);
1052 if ( sTmp
.Contains(sV
) )
1053 command
= sTmp
.AfterFirst(wxT('='));
1059 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo
& filetype
)
1063 wxString extensions
;
1064 const wxArrayString
& exts
= filetype
.GetExtensions();
1065 size_t nExts
= exts
.GetCount();
1066 for ( size_t nExt
= 0; nExt
< nExts
; nExt
++ )
1069 extensions
+= wxT(' ');
1071 extensions
+= exts
[nExt
];
1074 AddMimeTypeInfo(filetype
.GetMimeType(),
1076 filetype
.GetDescription());
1079 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString
& strMimeType
,
1080 const wxString
& strExtensions
,
1081 const wxString
& strDesc
)
1083 // reading mailcap may find image/* , while
1084 // reading mime.types finds image/gif and no match is made
1085 // this means all the get functions don't work fix this
1087 wxString sTmp
= strExtensions
;
1089 wxArrayString sExts
;
1090 sTmp
.Trim().Trim(false);
1092 while (!sTmp
.empty())
1094 sExts
.Add(sTmp
.AfterLast(wxT(' ')));
1095 sTmp
= sTmp
.BeforeLast(wxT(' '));
1098 AddToMimeData(strMimeType
, strIcon
, NULL
, sExts
, strDesc
, true);
1101 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
1107 size_t count
= m_aTypes
.GetCount();
1108 for ( size_t n
= 0; n
< count
; n
++ )
1110 // don't return template types from here (i.e. anything containg '*')
1111 const wxString
&type
= m_aTypes
[n
];
1112 if ( type
.Find(wxT('*')) == wxNOT_FOUND
)
1114 mimetypes
.Add(type
);
1118 return mimetypes
.GetCount();
1121 // ----------------------------------------------------------------------------
1122 // writing to MIME type files
1123 // ----------------------------------------------------------------------------
1125 bool wxMimeTypesManagerImpl::Unassociate(wxFileType
*ft
)
1129 wxArrayString sMimeTypes
;
1130 ft
->GetMimeTypes(sMimeTypes
);
1133 size_t nCount
= sMimeTypes
.GetCount();
1134 for (i
= 0; i
< nCount
; i
++)
1136 const wxString
&sMime
= sMimeTypes
.Item(i
);
1137 int nIndex
= m_aTypes
.Index(sMime
);
1138 if ( nIndex
== wxNOT_FOUND
)
1140 // error if we get here ??
1145 WriteMimeInfo(nIndex
, true);
1146 m_aTypes
.RemoveAt(nIndex
);
1147 m_aEntries
.RemoveAt(nIndex
);
1148 m_aExtensions
.RemoveAt(nIndex
);
1149 m_aDescriptions
.RemoveAt(nIndex
);
1150 m_aIcons
.RemoveAt(nIndex
);
1153 // check data integrity
1154 wxASSERT( m_aTypes
.GetCount() == m_aEntries
.GetCount() &&
1155 m_aTypes
.GetCount() == m_aExtensions
.GetCount() &&
1156 m_aTypes
.GetCount() == m_aIcons
.GetCount() &&
1157 m_aTypes
.GetCount() == m_aDescriptions
.GetCount() );
1163 // wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE