]> git.saurik.com Git - wxWidgets.git/blame - src/unix/mimetype.cpp
Fix wxHtmlHelpData::SetTempDir() to behave correctly without trailing slash.
[wxWidgets.git] / src / unix / mimetype.cpp
CommitLineData
b13d92d1 1/////////////////////////////////////////////////////////////////////////////
7520f3da 2// Name: src/unix/mimetype.cpp
b13d92d1
VZ
3// Purpose: classes and functions to manage MIME types
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 23.09.98
b13d92d1 7// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
65571936 8// Licence: wxWindows licence (part of wxExtra library)
b13d92d1
VZ
9/////////////////////////////////////////////////////////////////////////////
10
b13d92d1
VZ
11// for compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
7520f3da 15 #pragma hdrstop
ce4169a4
RR
16#endif
17
05f616ef 18#if wxUSE_MIMETYPE && wxUSE_FILE
ce4169a4 19
88a7a4e1
WS
20#include "wx/unix/mimetype.h"
21
ce4169a4 22#ifndef WX_PRECOMP
ad9835c9 23 #include "wx/dynarray.h"
7520f3da 24 #include "wx/string.h"
88a7a4e1 25 #include "wx/intl.h"
e4db172a 26 #include "wx/log.h"
de6185e2 27 #include "wx/utils.h"
7c2e5dec 28#endif
3d05544e 29
ce4169a4 30#include "wx/file.h"
2432b92d 31#include "wx/confbase.h"
b13d92d1 32
7dc3cc31 33#include "wx/ffile.h"
7dc3cc31 34#include "wx/dir.h"
7dc3cc31 35#include "wx/tokenzr.h"
c41dbc20 36#include "wx/iconloc.h"
1d529ef7 37#include "wx/filename.h"
88bbc332
RR
38#include "wx/app.h"
39#include "wx/apptrait.h"
b13d92d1 40
b13d92d1
VZ
41// other standard headers
42#include <ctype.h>
43
29886d1b 44class wxMimeTextFile
2b813b73
VZ
45{
46public:
4010aae1 47 wxMimeTextFile()
29886d1b
RR
48 {
49 }
4010aae1 50
29886d1b
RR
51 wxMimeTextFile(const wxString& fname)
52 {
53 m_fname = fname;
54 }
4010aae1 55
d39e2bbc 56 bool Open()
29886d1b 57 {
d39e2bbc 58 wxFFile file( m_fname );
29886d1b
RR
59 if (!file.IsOpened())
60 return false;
4010aae1 61
29886d1b
RR
62 size_t size = file.Length();
63 wxCharBuffer buffer( size );
64 file.Read( (void*) (const char*) buffer, size );
4010aae1 65
d39e2bbc 66 // Check for valid UTF-8 here?
29886d1b 67 wxString all = wxString::FromUTF8( buffer, size );
4010aae1 68
29886d1b
RR
69 wxStringTokenizer tok( all, "\n" );
70 while (tok.HasMoreTokens())
71 {
72 wxString t = tok.GetNextToken();
73 t.MakeLower();
05f616ef 74 if ((!!t) && (t.Find( "comment" ) != 0) && (t.Find( "#" ) != 0) && (t.Find( "generic" ) != 0))
29886d1b
RR
75 m_text.Add( t );
76 }
77 return true;
78 }
4010aae1 79
29886d1b
RR
80 unsigned int GetLineCount() const { return m_text.GetCount(); }
81 wxString &GetLine( unsigned int line ) { return m_text[line]; }
2b813b73 82
d9bbb2d8
VZ
83 int pIndexOf(const wxString& sSearch,
84 bool bIncludeComments = false,
85 int iStart = 0)
2b813b73 86 {
2b813b73
VZ
87 wxString sTest = sSearch;
88 sTest.MakeLower();
31383ebc 89 for(size_t i = iStart; i < GetLineCount(); i++)
2b813b73 90 {
29886d1b 91 wxString sLine = GetLine(i);
31383ebc 92 if(bIncludeComments || ! sLine.StartsWith(wxT("#")))
2b813b73 93 {
31383ebc
RR
94 if(sLine.StartsWith(sTest))
95 return (int)i;
2b813b73
VZ
96 }
97 }
31383ebc 98 return wxNOT_FOUND;
2b813b73
VZ
99 }
100
dfea7acc 101 wxString GetVerb(size_t i)
2b813b73 102 {
dfea7acc
DS
103 if (i > GetLineCount() )
104 return wxEmptyString;
105
2b813b73
VZ
106 wxString sTmp = GetLine(i).BeforeFirst(wxT('='));
107 return sTmp;
108 }
109
dfea7acc 110 wxString GetCmd(size_t i)
2b813b73 111 {
dfea7acc
DS
112 if (i > GetLineCount() )
113 return wxEmptyString;
114
2b813b73
VZ
115 wxString sTmp = GetLine(i).AfterFirst(wxT('='));
116 return sTmp;
117 }
4010aae1 118
29886d1b
RR
119private:
120 wxArrayString m_text;
121 wxString m_fname;
2b813b73
VZ
122};
123
f6bcfd97
BP
124// ----------------------------------------------------------------------------
125// constants
126// ----------------------------------------------------------------------------
127
128// MIME code tracing mask
dfea7acc 129#define TRACE_MIME wxT("mime")
f6bcfd97 130
2b813b73 131
d39e2bbc
RR
132// Read a XDG *.desktop file of type 'Application'
133void wxMimeTypesManagerImpl::LoadXDGApp(const wxString& filename)
2b813b73 134{
d39e2bbc 135 wxLogTrace(TRACE_MIME, wxT("loading XDG file %s"), filename.c_str());
2b813b73 136
d39e2bbc
RR
137 wxMimeTextFile file(filename);
138 if ( !file.Open() )
d9bbb2d8 139 return;
4010aae1 140
31383ebc 141 // Here, only type 'Application' should be considered.
29886d1b
RR
142 int nIndex = file.pIndexOf( "Type=" );
143 if (nIndex != wxNOT_FOUND && file.GetCmd(nIndex) != "application")
31383ebc 144 return;
dfea7acc 145
31383ebc 146 // The hidden entry specifies a file to be ignored.
29886d1b
RR
147 nIndex = file.pIndexOf( "Hidden=" );
148 if (nIndex != wxNOT_FOUND && file.GetCmd(nIndex) == "true")
31383ebc 149 return;
dfea7acc 150
31383ebc
RR
151 // Semicolon separated list of mime types handled by the application.
152 nIndex = file.pIndexOf( wxT("MimeType=") );
153 if (nIndex == wxNOT_FOUND)
154 return;
155 wxString mimetypes = file.GetCmd (nIndex);
dfea7acc 156
31383ebc
RR
157 // Name of the application
158 wxString nameapp;
159 nIndex = wxNOT_FOUND;
160#if wxUSE_INTL // try "Name[locale name]" first
161 wxLocale *locale = wxGetLocale();
162 if ( locale )
9a83f860 163 nIndex = file.pIndexOf(wxT("Name[")+locale->GetName()+wxT("]="));
31383ebc
RR
164#endif // wxUSE_INTL
165 if(nIndex == wxNOT_FOUND)
166 nIndex = file.pIndexOf( wxT("Name=") );
167 if(nIndex != wxNOT_FOUND)
168 nameapp = file.GetCmd(nIndex);
169
170 // Icon of the application.
171 wxString nameicon, namemini;
172 nIndex = wxNOT_FOUND;
173#if wxUSE_INTL // try "Icon[locale name]" first
174 if ( locale )
9a83f860 175 nIndex = file.pIndexOf(wxT("Icon[")+locale->GetName()+wxT("]="));
31383ebc
RR
176#endif // wxUSE_INTL
177 if(nIndex == wxNOT_FOUND)
178 nIndex = file.pIndexOf( wxT("Icon=") );
179 if(nIndex != wxNOT_FOUND) {
180 nameicon = wxString(wxT("--icon ")) + file.GetCmd(nIndex);
181 namemini = wxString(wxT("--miniicon ")) + file.GetCmd(nIndex);
182 }
183
184 // Replace some of the field code in the 'Exec' entry.
185 // TODO: deal with %d, %D, %n, %N, %k and %v (but last one is deprecated)
186 nIndex = file.pIndexOf( wxT("Exec=") );
187 if (nIndex == wxNOT_FOUND)
188 return;
189 wxString sCmd = file.GetCmd(nIndex);
190 // we expect %f; others including %F and %U and %u are possible
191 sCmd.Replace(wxT("%F"), wxT("%f"));
192 sCmd.Replace(wxT("%U"), wxT("%f"));
193 sCmd.Replace(wxT("%u"), wxT("%f"));
194 if (0 == sCmd.Replace ( wxT("%f"), wxT("%s") ))
195 sCmd = sCmd + wxT(" %s");
196 sCmd.Replace(wxT("%c"), nameapp);
197 sCmd.Replace(wxT("%i"), nameicon);
198 sCmd.Replace(wxT("%m"), namemini);
199
9a83f860 200 wxStringTokenizer tokenizer(mimetypes, wxT(";"));
31383ebc
RR
201 while(tokenizer.HasMoreTokens()) {
202 wxString mimetype = tokenizer.GetNextToken().Lower();
309aefbd 203 nIndex = m_aTypes.Index(mimetype);
31383ebc
RR
204 if(nIndex != wxNOT_FOUND) { // is this a known MIME type?
205 wxMimeTypeCommands* entry = m_aEntries[nIndex];
206 entry->AddOrReplaceVerb(wxT("open"), sCmd);
10274336 207 }
31383ebc
RR
208 }
209}
d0ee33f5 210
d39e2bbc 211void wxMimeTypesManagerImpl::LoadXDGAppsFilesFromDir(const wxString& dirname)
31383ebc 212{
6fc93e9b
JS
213 // Don't complain if we don't have permissions to read - it confuses users
214 wxLogNull logNull;
215
31383ebc
RR
216 if(! wxDir::Exists(dirname))
217 return;
218 wxDir dir(dirname);
219 if ( !dir.IsOpened() )
220 return;
d0ee33f5 221
31383ebc
RR
222 wxString filename;
223 // Look into .desktop files
9a83f860 224 bool cont = dir.GetFirst(&filename, wxT("*.desktop"), wxDIR_FILES);
29886d1b
RR
225 while (cont)
226 {
31383ebc 227 wxFileName p(dirname, filename);
d39e2bbc 228 LoadXDGApp( p.GetFullPath() );
31383ebc
RR
229 cont = dir.GetNext(&filename);
230 }
d39e2bbc 231
4010aae1 232#if 0
d39e2bbc
RR
233 // RR: I'm not sure this makes any sense. On my system we'll just
234 // scan the YAST2 and other useless directories
4010aae1 235
31383ebc
RR
236 // Look recursively into subdirs
237 cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS);
29886d1b
RR
238 while (cont)
239 {
31383ebc
RR
240 wxFileName p(dirname, wxEmptyString);
241 p.AppendDir(filename);
d39e2bbc 242 LoadXDGAppsFilesFromDir( p.GetPath() );
31383ebc
RR
243 cont = dir.GetNext(&filename);
244 }
d39e2bbc 245#endif
31383ebc 246}
d0ee33f5 247
b9517a0a 248
05f616ef
RR
249void wxMimeTypesManagerImpl::LoadXDGGlobs(const wxString& filename)
250{
4010aae1
VZ
251 if ( !wxFileName::FileExists(filename) )
252 return;
253
05f616ef
RR
254 wxLogTrace(TRACE_MIME, wxT("loading XDG globs file from %s"), filename.c_str());
255
256 wxMimeTextFile file(filename);
257 if ( !file.Open() )
258 return;
259
d8a57f5e
RR
260 size_t i;
261 for (i = 0; i < file.GetLineCount(); i++)
262 {
263 wxStringTokenizer tok( file.GetLine(i), ":" );
264 wxString mime = tok.GetNextToken();
265 wxString ext = tok.GetNextToken();
266 ext.Remove( 0, 2 );
267 wxArrayString exts;
268 exts.Add( ext );
03647350 269
98359ac7 270 AddToMimeData(mime, wxEmptyString, NULL, exts, wxEmptyString, true );
d8a57f5e 271 }
05f616ef
RR
272}
273
2b813b73
VZ
274// ----------------------------------------------------------------------------
275// wxFileTypeImpl (Unix)
276// ----------------------------------------------------------------------------
277
2b813b73 278wxString wxFileTypeImpl::GetExpandedCommand(const wxString & verb, const wxFileType::MessageParameters& params) const
b9517a0a 279{
2b813b73
VZ
280 wxString sTmp;
281 size_t i = 0;
678ebfcd 282 while ( (i < m_index.GetCount() ) && sTmp.empty() )
b9517a0a 283 {
dfea7acc
DS
284 sTmp = m_manager->GetCommand( verb, m_index[i] );
285 i++;
b9517a0a
VZ
286 }
287
2b813b73
VZ
288 return wxFileType::ExpandCommand(sTmp, params);
289}
b9517a0a 290
da0766ab 291bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
2b813b73
VZ
292{
293 wxString sTmp;
294 size_t i = 0;
678ebfcd 295 while ( (i < m_index.GetCount() ) && sTmp.empty() )
c786f763
VZ
296 {
297 sTmp = m_manager->m_aIcons[m_index[i]];
dfea7acc 298 i++;
c786f763 299 }
dfea7acc
DS
300
301 if ( sTmp.empty() )
d0ee33f5 302 return false;
b9517a0a 303
da0766ab 304 if ( iconLoc )
c786f763 305 {
a49686b4 306 iconLoc->SetFileName(sTmp);
2b813b73 307 }
c786f763 308
d0ee33f5 309 return true;
c786f763 310}
2b813b73 311
dfea7acc 312bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
2b813b73
VZ
313{
314 mimeTypes.Clear();
15d4b8cd
RR
315 size_t nCount = m_index.GetCount();
316 for (size_t i = 0; i < nCount; i++)
2b813b73 317 mimeTypes.Add(m_manager->m_aTypes[m_index[i]]);
dfea7acc 318
d0ee33f5 319 return true;
2b813b73
VZ
320}
321
2b813b73
VZ
322size_t wxFileTypeImpl::GetAllCommands(wxArrayString *verbs,
323 wxArrayString *commands,
324 const wxFileType::MessageParameters& params) const
325{
2b813b73
VZ
326 wxString vrb, cmd, sTmp;
327 size_t count = 0;
678ebfcd 328 wxMimeTypeCommands * sPairs;
2b813b73
VZ
329
330 // verbs and commands have been cleared already in mimecmn.cpp...
331 // if we find no entries in the exact match, try the inexact match
7c2e5dec 332 for (size_t n = 0; ((count == 0) && (n < m_index.GetCount())); n++)
2b813b73
VZ
333 {
334 // list of verb = command pairs for this mimetype
335 sPairs = m_manager->m_aEntries [m_index[n]];
336 size_t i;
dfea7acc
DS
337 for ( i = 0; i < sPairs->GetCount(); i++ )
338 {
339 vrb = sPairs->GetVerb(i);
7c2e5dec 340 // some gnome entries have "." inside
dfea7acc
DS
341 vrb = vrb.AfterLast(wxT('.'));
342 cmd = sPairs->GetCmd(i);
343 if (! cmd.empty() )
2b813b73 344 {
dfea7acc 345 cmd = wxFileType::ExpandCommand(cmd, params);
7c2e5dec 346 count++;
dfea7acc
DS
347 if ( vrb.IsSameAs(wxT("open")))
348 {
d6a7ca31
VZ
349 if ( verbs )
350 verbs->Insert(vrb, 0u);
351 if ( commands )
352 commands ->Insert(cmd, 0u);
dfea7acc
DS
353 }
354 else
355 {
d6a7ca31
VZ
356 if ( verbs )
357 verbs->Add(vrb);
358 if ( commands )
359 commands->Add(cmd);
dfea7acc
DS
360 }
361 }
2b813b73 362 }
2b813b73 363 }
2b813b73 364
dfea7acc 365 return count;
2b813b73
VZ
366}
367
368bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
369{
86501081 370 const wxString strExtensions = m_manager->GetExtension(m_index[0]);
2b813b73
VZ
371 extensions.Empty();
372
1c4cd9e0 373 // one extension in the space or comma-delimited list
2b813b73 374 wxString strExt;
86501081
VS
375 wxString::const_iterator end = strExtensions.end();
376 for ( wxString::const_iterator p = strExtensions.begin(); /* nothing */; ++p )
dfea7acc 377 {
86501081 378 if ( p == end || *p == wxT(' ') || *p == wxT(',') )
dfea7acc
DS
379 {
380 if ( !strExt.empty() )
381 {
2b813b73
VZ
382 extensions.Add(strExt);
383 strExt.Empty();
384 }
7c2e5dec
DS
385 //else: repeated spaces
386 // (shouldn't happen, but it's not that important if it does happen)
2b813b73 387
86501081 388 if ( p == end )
2b813b73
VZ
389 break;
390 }
dfea7acc
DS
391 else if ( *p == wxT('.') )
392 {
2b813b73 393 // remove the dot from extension (but only if it's the first char)
dfea7acc
DS
394 if ( !strExt.empty() )
395 {
2b813b73
VZ
396 strExt += wxT('.');
397 }
398 //else: no, don't append it
399 }
dfea7acc
DS
400 else
401 {
2b813b73
VZ
402 strExt += *p;
403 }
404 }
405
d0ee33f5 406 return true;
2b813b73
VZ
407}
408
7c2e5dec 409// set an arbitrary command:
2b813b73 410// could adjust the code to ask confirmation if it already exists and
d0ee33f5 411// overwriteprompt is true, but this is currently ignored as *Associate* has
2b813b73 412// no overwrite prompt
17a1ebd1
VZ
413bool
414wxFileTypeImpl::SetCommand(const wxString& cmd,
415 const wxString& verb,
416 bool WXUNUSED(overwriteprompt))
d84afea9 417{
2b813b73 418 wxArrayString strExtensions;
678ebfcd 419 wxString strDesc, strIcon;
2b813b73 420
2b813b73 421 wxArrayString strTypes;
dfea7acc 422 GetMimeTypes(strTypes);
926ce9e3 423 if ( strTypes.IsEmpty() )
dfea7acc 424 return false;
2b813b73 425
926ce9e3
VZ
426 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
427 entry->Add(verb + wxT("=") + cmd + wxT(" %s "));
428
dda522bf 429 bool ok = false;
15d4b8cd
RR
430 size_t nCount = strTypes.GetCount();
431 for ( size_t i = 0; i < nCount; i++ )
d84afea9 432 {
dda522bf
VZ
433 if ( m_manager->DoAssociation
434 (
435 strTypes[i],
436 strIcon,
437 entry,
438 strExtensions,
439 strDesc
440 ) )
441 {
442 // DoAssociation() took ownership of entry, don't delete it below
443 ok = true;
444 }
d84afea9 445 }
2b813b73 446
dda522bf
VZ
447 if ( !ok )
448 delete entry;
449
dfea7acc 450 return ok;
d84afea9 451}
2b813b73 452
15d4b8cd 453// ignore index on the grounds that we only have one icon in a Unix file
17a1ebd1 454bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int WXUNUSED(index))
d84afea9 455{
dfea7acc
DS
456 if (strIcon.empty())
457 return false;
458
2b813b73
VZ
459 wxArrayString strExtensions;
460 wxString strDesc;
461
2b813b73 462 wxArrayString strTypes;
dfea7acc 463 GetMimeTypes(strTypes);
cb0b7b7d 464 if ( strTypes.IsEmpty() )
dfea7acc 465 return false;
2b813b73 466
cb0b7b7d 467 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
2f769cb9 468 bool ok = false;
15d4b8cd
RR
469 size_t nCount = strTypes.GetCount();
470 for ( size_t i = 0; i < nCount; i++ )
d84afea9 471 {
2f769cb9
VZ
472 if ( m_manager->DoAssociation
473 (
cb0b7b7d
VZ
474 strTypes[i],
475 strIcon,
476 entry,
477 strExtensions,
478 strDesc
2f769cb9 479 ) )
cb0b7b7d 480 {
2f769cb9
VZ
481 // we don't need to free entry now, DoAssociation() took ownership
482 // of it
483 ok = true;
cb0b7b7d 484 }
d84afea9 485 }
2b813b73 486
2f769cb9
VZ
487 if ( !ok )
488 delete entry;
489
dfea7acc 490 return ok;
d84afea9
GD
491}
492
2b813b73
VZ
493// ----------------------------------------------------------------------------
494// wxMimeTypesManagerImpl (Unix)
495// ----------------------------------------------------------------------------
496
2b813b73
VZ
497wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
498{
d0ee33f5 499 m_initialized = false;
2b813b73
VZ
500}
501
1d529ef7
RR
502void wxMimeTypesManagerImpl::InitIfNeeded()
503{
504 if ( !m_initialized )
505 {
506 // set the flag first to prevent recursion
d0ee33f5 507 m_initialized = true;
4010aae1 508
6e3f8e61
VZ
509 int mailcapStyles = wxMAILCAP_ALL;
510 if ( wxAppTraits * const traits = wxApp::GetTraitsIfExists() )
511 {
512 wxString wm = traits->GetDesktopEnvironment();
4010aae1 513
6e3f8e61
VZ
514 if ( wm == "KDE" )
515 mailcapStyles = wxMAILCAP_KDE;
516 else if ( wm == "GNOME" )
517 mailcapStyles = wxMAILCAP_GNOME;
518 //else: unknown, use the default
519 }
520
521 Initialize(mailcapStyles);
1d529ef7
RR
522 }
523}
524
05f616ef
RR
525
526
2b813b73
VZ
527// read system and user mailcaps and other files
528void wxMimeTypesManagerImpl::Initialize(int mailcapStyles,
529 const wxString& sExtraDir)
530{
29886d1b
RR
531#ifdef __VMS
532 // XDG tables are never installed on OpenVMS
533 return;
9bc0aaae 534#else
2b813b73 535
05f616ef
RR
536 // Read MIME type - extension associations
537 LoadXDGGlobs( "/usr/share/mime/globs" );
397e7f98 538 LoadXDGGlobs( "/usr/local/share/mime/globs" );
05f616ef
RR
539
540 // Load desktop files for XDG, and then override them with the defaults.
84513dcb
JS
541 // We will override them one desktop file at a time, rather
542 // than one mime type at a time, but it should be a reasonable
543 // heuristic.
84513dcb 544 {
d39e2bbc 545 wxString xdgDataHome = wxGetenv("XDG_DATA_HOME");
84513dcb 546 if ( xdgDataHome.empty() )
d39e2bbc
RR
547 xdgDataHome = wxGetHomeDir() + "/.local/share";
548 wxString xdgDataDirs = wxGetenv("XDG_DATA_DIRS");
84513dcb 549 if ( xdgDataDirs.empty() )
d39e2bbc
RR
550 {
551 xdgDataDirs = "/usr/local/share:/usr/share";
552 if (mailcapStyles & wxMAILCAP_GNOME)
553 xdgDataDirs += ":/usr/share/gnome:/opt/gnome/share";
554 if (mailcapStyles & wxMAILCAP_KDE)
555 xdgDataDirs += ":/usr/share/kde3:/opt/kde3/share";
556 }
557 if ( !sExtraDir.empty() )
558 {
559 xdgDataDirs += ':';
560 xdgDataDirs += sExtraDir;
561 }
4010aae1 562
84513dcb 563 wxArrayString dirs;
d39e2bbc 564 wxStringTokenizer tokenizer(xdgDataDirs, ":");
84513dcb
JS
565 while ( tokenizer.HasMoreTokens() )
566 {
567 wxString p = tokenizer.GetNextToken();
568 dirs.Add(p);
569 }
570 dirs.insert(dirs.begin(), xdgDataHome);
571
572 wxString defaultsList;
573 size_t i;
574 for (i = 0; i < dirs.GetCount(); i++)
575 {
29886d1b
RR
576 wxString f = dirs[i];
577 if (f.Last() != '/') f += '/';
578 f += "applications/defaults.list";
84513dcb
JS
579 if (wxFileExists(f))
580 {
581 defaultsList = f;
582 break;
583 }
584 }
585
586 // Load application files and associate them to corresponding mime types.
587 size_t nDirs = dirs.GetCount();
588 for (size_t nDir = 0; nDir < nDirs; nDir++)
589 {
29886d1b
RR
590 wxString dirStr = dirs[nDir];
591 if (dirStr.Last() != '/') dirStr += '/';
592 dirStr += "applications";
d39e2bbc 593 LoadXDGAppsFilesFromDir(dirStr);
84513dcb
JS
594 }
595
596 if (!defaultsList.IsEmpty())
597 {
598 wxArrayString deskTopFilesSeen;
599
600 wxMimeTextFile textfile(defaultsList);
601 if ( textfile.Open() )
602 {
603 int nIndex = textfile.pIndexOf( wxT("[Default Applications]") );
604 if (nIndex != wxNOT_FOUND)
605 {
606 for (i = nIndex+1; i < textfile.GetLineCount(); i++)
607 {
29886d1b 608 if (textfile.GetLine(i).Find(wxT("=")) != wxNOT_FOUND)
84513dcb 609 {
84513dcb 610 wxString desktopFile = textfile.GetCmd(i);
4010aae1 611
84513dcb
JS
612 if (deskTopFilesSeen.Index(desktopFile) == wxNOT_FOUND)
613 {
614 deskTopFilesSeen.Add(desktopFile);
615 size_t j;
29886d1b
RR
616 for (j = 0; j < dirs.GetCount(); j++)
617 {
618 wxString desktopPath = dirs[j];
d39e2bbc
RR
619 if (desktopPath.Last() != '/') desktopPath += '/';
620 desktopPath += "applications/";
29886d1b 621 desktopPath += desktopFile;
4010aae1 622
29886d1b 623 if (wxFileExists(desktopPath))
d39e2bbc 624 LoadXDGApp(desktopPath);
29886d1b
RR
625 }
626 }
627 }
2b813b73 628 }
29886d1b 629 }
2b813b73 630 }
b13d92d1 631 }
b13d92d1 632 }
9bc0aaae 633#endif
29886d1b 634}
dfea7acc 635
29886d1b
RR
636// clear data so you can read another group of WM files
637void wxMimeTypesManagerImpl::ClearData()
638{
639 m_aTypes.Clear();
640 m_aIcons.Clear();
641 m_aExtensions.Clear();
642 m_aDescriptions.Clear();
643
644 WX_CLEAR_ARRAY(m_aEntries);
645 m_aEntries.Empty();
646}
647
648wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl()
649{
650 ClearData();
b13d92d1
VZ
651}
652
dfea7acc 653wxFileType * wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
b9517a0a 654{
2b813b73 655 InitIfNeeded();
b9517a0a 656
dfea7acc
DS
657 wxString strType = ftInfo.GetMimeType();
658 wxString strDesc = ftInfo.GetDescription();
659 wxString strIcon = ftInfo.GetIconFile();
2b813b73 660
dfea7acc 661 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
2b813b73 662
678ebfcd 663 if ( ! ftInfo.GetOpenCommand().empty())
dfea7acc
DS
664 entry->Add(wxT("open=") + ftInfo.GetOpenCommand() + wxT(" %s "));
665 if ( ! ftInfo.GetPrintCommand().empty())
666 entry->Add(wxT("print=") + ftInfo.GetPrintCommand() + wxT(" %s "));
2b813b73
VZ
667
668 // now find where these extensions are in the data store and remove them
dfea7acc 669 wxArrayString sA_Exts = ftInfo.GetExtensions();
2b813b73
VZ
670 wxString sExt, sExtStore;
671 size_t i, nIndex;
15d4b8cd
RR
672 size_t nExtCount = sA_Exts.GetCount();
673 for (i=0; i < nExtCount; i++)
dfea7acc 674 {
2b813b73 675 sExt = sA_Exts.Item(i);
dfea7acc
DS
676
677 // clean up to just a space before and after
d0ee33f5 678 sExt.Trim().Trim(false);
2b813b73 679 sExt = wxT(' ') + sExt + wxT(' ');
15d4b8cd
RR
680 size_t nCount = m_aExtensions.GetCount();
681 for (nIndex = 0; nIndex < nCount; nIndex++)
dfea7acc 682 {
2b813b73 683 sExtStore = m_aExtensions.Item(nIndex);
dfea7acc
DS
684 if (sExtStore.Replace(sExt, wxT(" ") ) > 0)
685 m_aExtensions.Item(nIndex) = sExtStore;
686 }
2b813b73 687 }
b9517a0a 688
dfea7acc 689 if ( !DoAssociation(strType, strIcon, entry, sA_Exts, strDesc) )
2b813b73 690 return NULL;
4d2976ad 691
2b813b73 692 return GetFileTypeFromMimeType(strType);
4d2976ad
VS
693}
694
2b813b73
VZ
695bool wxMimeTypesManagerImpl::DoAssociation(const wxString& strType,
696 const wxString& strIcon,
678ebfcd 697 wxMimeTypeCommands *entry,
2b813b73
VZ
698 const wxArrayString& strExtensions,
699 const wxString& strDesc)
b13d92d1 700{
d0ee33f5 701 int nIndex = AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, true);
7520f3da 702
2b813b73 703 if ( nIndex == wxNOT_FOUND )
d0ee33f5 704 return false;
b13d92d1 705
d39e2bbc 706 return true;
a6c65e88
VZ
707}
708
2b813b73
VZ
709int wxMimeTypesManagerImpl::AddToMimeData(const wxString& strType,
710 const wxString& strIcon,
678ebfcd 711 wxMimeTypeCommands *entry,
2b813b73
VZ
712 const wxArrayString& strExtensions,
713 const wxString& strDesc,
678ebfcd 714 bool replaceExisting)
b13d92d1 715{
2b813b73 716 InitIfNeeded();
b13d92d1 717
2b813b73 718 // ensure mimetype is always lower case
678ebfcd
VZ
719 wxString mimeType = strType.Lower();
720
721 // is this a known MIME type?
2b813b73
VZ
722 int nIndex = m_aTypes.Index(mimeType);
723 if ( nIndex == wxNOT_FOUND )
724 {
98359ac7 725 // We put MIME types containing "application" at the end, so that
e91d8a28 726 // if the MIME type for the extension "htm" is searched for, it will
98359ac7
RR
727 // rather find "text/html" than "application/x-mozilla-bookmarks".
728 if (mimeType.Find( "application" ) == 0)
729 {
730 // new file type
731 m_aTypes.Add(mimeType);
732 m_aIcons.Add(strIcon);
733 m_aEntries.Add(entry ? entry : new wxMimeTypeCommands);
b13d92d1 734
98359ac7
RR
735 // change nIndex so we can use it below to add the extensions
736 m_aExtensions.Add(wxEmptyString);
737 nIndex = m_aExtensions.size() - 1;
678ebfcd 738
98359ac7
RR
739 m_aDescriptions.Add(strDesc);
740 }
741 else
742 {
743 // new file type
744 m_aTypes.Insert(mimeType,0);
745 m_aIcons.Insert(strIcon,0);
746 m_aEntries.Insert(entry ? entry : new wxMimeTypeCommands,0);
747
748 // change nIndex so we can use it below to add the extensions
749 m_aExtensions.Insert(wxEmptyString,0);
750 nIndex = 0;
751
752 m_aDescriptions.Insert(strDesc,0);
753 }
b13d92d1 754 }
678ebfcd 755 else // yes, we already have it
2b813b73 756 {
678ebfcd 757 if ( replaceExisting )
2b813b73
VZ
758 {
759 // if new description change it
678ebfcd 760 if ( !strDesc.empty())
2b813b73
VZ
761 m_aDescriptions[nIndex] = strDesc;
762
763 // if new icon change it
678ebfcd 764 if ( !strIcon.empty())
2b813b73
VZ
765 m_aIcons[nIndex] = strIcon;
766
678ebfcd
VZ
767 if ( entry )
768 {
769 delete m_aEntries[nIndex];
770 m_aEntries[nIndex] = entry;
771 }
2b813b73 772 }
678ebfcd 773 else // add data we don't already have ...
2b813b73 774 {
2b813b73 775 // if new description add only if none
678ebfcd 776 if ( m_aDescriptions[nIndex].empty() )
2b813b73
VZ
777 m_aDescriptions[nIndex] = strDesc;
778
779 // if new icon and no existing icon
dfea7acc 780 if ( m_aIcons[nIndex].empty() )
2b813b73
VZ
781 m_aIcons[nIndex] = strIcon;
782
2b813b73 783 // add any new entries...
678ebfcd 784 if ( entry )
2b813b73 785 {
7c2e5dec 786 wxMimeTypeCommands *entryOld = m_aEntries[nIndex];
678ebfcd
VZ
787
788 size_t count = entry->GetCount();
789 for ( size_t i = 0; i < count; i++ )
790 {
791 const wxString& verb = entry->GetVerb(i);
792 if ( !entryOld->HasVerb(verb) )
793 {
794 entryOld->AddOrReplaceVerb(verb, entry->GetCmd(i));
795 }
796 }
2b293fc7
VZ
797
798 // as we don't store it anywhere, it won't be deleted later as
799 // usual -- do it immediately instead
800 delete entry;
2b813b73
VZ
801 }
802 }
b13d92d1 803 }
4d2976ad 804
678ebfcd
VZ
805 // always add the extensions to this mimetype
806 wxString& exts = m_aExtensions[nIndex];
807
808 // add all extensions we don't have yet
15d4b8cd 809 wxString ext;
678ebfcd
VZ
810 size_t count = strExtensions.GetCount();
811 for ( size_t i = 0; i < count; i++ )
812 {
15d4b8cd
RR
813 ext = strExtensions[i];
814 ext += wxT(' ');
678ebfcd
VZ
815
816 if ( exts.Find(ext) == wxNOT_FOUND )
817 {
818 exts += ext;
819 }
820 }
821
2b813b73 822 // check data integrity
b4a980f4
VZ
823 wxASSERT( m_aTypes.GetCount() == m_aEntries.GetCount() &&
824 m_aTypes.GetCount() == m_aExtensions.GetCount() &&
825 m_aTypes.GetCount() == m_aIcons.GetCount() &&
826 m_aTypes.GetCount() == m_aDescriptions.GetCount() );
cf471cab 827
2b813b73 828 return nIndex;
cf471cab
VS
829}
830
dfea7acc 831wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
b13d92d1 832{
678ebfcd 833 if (ext.empty() )
2b813b73
VZ
834 return NULL;
835
a6c65e88
VZ
836 InitIfNeeded();
837
1ee17e1c 838 size_t count = m_aExtensions.GetCount();
2b813b73 839 for ( size_t n = 0; n < count; n++ )
678ebfcd 840 {
dfea7acc 841 wxStringTokenizer tk(m_aExtensions[n], wxT(' '));
1ee17e1c 842
678ebfcd
VZ
843 while ( tk.HasMoreTokens() )
844 {
1ee17e1c 845 // consider extensions as not being case-sensitive
d0ee33f5 846 if ( tk.GetNextToken().IsSameAs(ext, false /* no case */) )
678ebfcd 847 {
1ee17e1c 848 // found
678ebfcd 849 wxFileType *fileType = new wxFileType;
1ee17e1c 850 fileType->m_impl->Init(this, n);
03647350 851
678ebfcd 852 return fileType;
1ee17e1c
VZ
853 }
854 }
855 }
b13d92d1 856
678ebfcd 857 return NULL;
b13d92d1
VZ
858}
859
7c2e5dec 860wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
b13d92d1 861{
a6c65e88
VZ
862 InitIfNeeded();
863
2b813b73 864 wxFileType * fileType = NULL;
b13d92d1
VZ
865 // mime types are not case-sensitive
866 wxString mimetype(mimeType);
867 mimetype.MakeLower();
868
869 // first look for an exact match
870 int index = m_aTypes.Index(mimetype);
4010aae1 871
2b813b73
VZ
872 if ( index != wxNOT_FOUND )
873 {
874 fileType = new wxFileType;
875 fileType->m_impl->Init(this, index);
876 }
877
878 // then try to find "text/*" as match for "text/plain" (for example)
879 // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
880 // the whole string - ok.
881
882 index = wxNOT_FOUND;
883 wxString strCategory = mimetype.BeforeFirst(wxT('/'));
884
b4a980f4 885 size_t nCount = m_aTypes.GetCount();
dfea7acc
DS
886 for ( size_t n = 0; n < nCount; n++ )
887 {
2b813b73 888 if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
dfea7acc
DS
889 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") )
890 {
2b813b73
VZ
891 index = n;
892 break;
b13d92d1
VZ
893 }
894 }
895
2b813b73 896 if ( index != wxNOT_FOUND )
dfea7acc
DS
897 {
898 // don't throw away fileType that was already found
899 if (!fileType)
7bc5ddc6 900 fileType = new wxFileType;
b13d92d1 901 fileType->m_impl->Init(this, index);
b13d92d1 902 }
dfea7acc 903
2b813b73
VZ
904 return fileType;
905}
906
2b813b73
VZ
907wxString wxMimeTypesManagerImpl::GetCommand(const wxString & verb, size_t nIndex) const
908{
909 wxString command, testcmd, sV, sTmp;
910 sV = verb + wxT("=");
dfea7acc 911
2b813b73 912 // list of verb = command pairs for this mimetype
678ebfcd 913 wxMimeTypeCommands * sPairs = m_aEntries [nIndex];
2b813b73
VZ
914
915 size_t i;
15d4b8cd
RR
916 size_t nCount = sPairs->GetCount();
917 for ( i = 0; i < nCount; i++ )
2b813b73 918 {
678ebfcd
VZ
919 sTmp = sPairs->GetVerbCmd (i);
920 if ( sTmp.Contains(sV) )
921 command = sTmp.AfterFirst(wxT('='));
b13d92d1 922 }
dfea7acc 923
2b813b73 924 return command;
b13d92d1
VZ
925}
926
8e124873
VZ
927void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
928{
a6c65e88
VZ
929 InitIfNeeded();
930
3f1aaa16 931 wxString extensions;
8e124873
VZ
932 const wxArrayString& exts = filetype.GetExtensions();
933 size_t nExts = exts.GetCount();
dfea7acc
DS
934 for ( size_t nExt = 0; nExt < nExts; nExt++ )
935 {
936 if ( nExt > 0 )
223d09f6 937 extensions += wxT(' ');
dfea7acc 938
8e124873
VZ
939 extensions += exts[nExt];
940 }
941
942 AddMimeTypeInfo(filetype.GetMimeType(),
943 extensions,
944 filetype.GetDescription());
8e124873
VZ
945}
946
947void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
948 const wxString& strExtensions,
949 const wxString& strDesc)
950{
2b813b73
VZ
951 // reading mailcap may find image/* , while
952 // reading mime.types finds image/gif and no match is made
953 // this means all the get functions don't work fix this
954 wxString strIcon;
955 wxString sTmp = strExtensions;
a6c65e88 956
2b813b73 957 wxArrayString sExts;
d0ee33f5 958 sTmp.Trim().Trim(false);
2b813b73 959
678ebfcd 960 while (!sTmp.empty())
2b813b73 961 {
dfea7acc 962 sExts.Add(sTmp.AfterLast(wxT(' ')));
2b813b73 963 sTmp = sTmp.BeforeLast(wxT(' '));
8e124873 964 }
2b813b73 965
dfea7acc 966 AddToMimeData(strMimeType, strIcon, NULL, sExts, strDesc, true);
8e124873
VZ
967}
968
696e1ea0 969size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
1b986aef 970{
a6c65e88
VZ
971 InitIfNeeded();
972
54acce90
VZ
973 mimetypes.Empty();
974
54acce90
VZ
975 size_t count = m_aTypes.GetCount();
976 for ( size_t n = 0; n < count; n++ )
977 {
978 // don't return template types from here (i.e. anything containg '*')
15d4b8cd 979 const wxString &type = m_aTypes[n];
dfea7acc 980 if ( type.Find(wxT('*')) == wxNOT_FOUND )
54acce90
VZ
981 {
982 mimetypes.Add(type);
983 }
984 }
1b986aef 985
54acce90 986 return mimetypes.GetCount();
1b986aef
VZ
987}
988
a6c65e88
VZ
989// ----------------------------------------------------------------------------
990// writing to MIME type files
991// ----------------------------------------------------------------------------
992
2b813b73 993bool wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
a6c65e88 994{
9413e1e3
VZ
995 InitIfNeeded();
996
2b813b73 997 wxArrayString sMimeTypes;
dfea7acc 998 ft->GetMimeTypes(sMimeTypes);
a6c65e88 999
2b813b73 1000 size_t i;
15d4b8cd
RR
1001 size_t nCount = sMimeTypes.GetCount();
1002 for (i = 0; i < nCount; i ++)
2b813b73 1003 {
15d4b8cd 1004 const wxString &sMime = sMimeTypes.Item(i);
dfea7acc 1005 int nIndex = m_aTypes.Index(sMime);
2b813b73
VZ
1006 if ( nIndex == wxNOT_FOUND)
1007 {
1008 // error if we get here ??
d0ee33f5 1009 return false;
2b813b73
VZ
1010 }
1011 else
1012 {
e9d9f136
VZ
1013 m_aTypes.RemoveAt(nIndex);
1014 m_aEntries.RemoveAt(nIndex);
1015 m_aExtensions.RemoveAt(nIndex);
1016 m_aDescriptions.RemoveAt(nIndex);
1017 m_aIcons.RemoveAt(nIndex);
2b813b73
VZ
1018 }
1019 }
1020 // check data integrity
b4a980f4
VZ
1021 wxASSERT( m_aTypes.GetCount() == m_aEntries.GetCount() &&
1022 m_aTypes.GetCount() == m_aExtensions.GetCount() &&
1023 m_aTypes.GetCount() == m_aIcons.GetCount() &&
1024 m_aTypes.GetCount() == m_aDescriptions.GetCount() );
2b813b73 1025
d0ee33f5 1026 return true;
a6c65e88
VZ
1027}
1028
8e124873 1029#endif
05f616ef 1030 // wxUSE_MIMETYPE && wxUSE_FILE