Removed most of the pre-XDG MIME code from the Unix implementation, many speed-ups...
[wxWidgets.git] / src / unix / mimetype.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/mimetype.cpp
3 // Purpose: classes and functions to manage MIME types
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 23.09.98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
11
12 // for compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE
20
21 #include "wx/unix/mimetype.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/dynarray.h"
25 #include "wx/string.h"
26 #include "wx/intl.h"
27 #include "wx/log.h"
28 #include "wx/utils.h"
29 #endif
30
31 #include "wx/file.h"
32 #include "wx/confbase.h"
33
34 #include "wx/ffile.h"
35 #include "wx/textfile.h"
36 #include "wx/dir.h"
37 #include "wx/tokenzr.h"
38 #include "wx/iconloc.h"
39 #include "wx/filename.h"
40 #include "wx/app.h"
41 #include "wx/apptrait.h"
42
43 // other standard headers
44 #include <ctype.h>
45
46 class wxMimeTextFile
47 {
48 public:
49 wxMimeTextFile()
50 {
51 }
52
53 wxMimeTextFile(const wxString& fname)
54 {
55 m_fname = fname;
56 }
57
58 void AddLine( const wxString &line ) { }
59
60 bool Open( const wxString &fname )
61 {
62 m_fname = fname;
63 wxFFile file( fname );
64 if (!file.IsOpened())
65 return false;
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 );
70
71 wxStringTokenizer tok( all, "\n" );
72 while (tok.HasMoreTokens())
73 {
74 wxString t = tok.GetNextToken();
75 t.MakeLower();
76 if ((!!t) && (t.Find( "comment" ) != 0) && (t.Find( "generic" ) != 0))
77 m_text.Add( t );
78 }
79 return true;
80 }
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; }
85
86 unsigned int GetLineCount() const { return m_text.GetCount(); }
87 wxString &GetLine( unsigned int line ) { return m_text[line]; }
88
89 int pIndexOf(const wxString& sSearch,
90 bool bIncludeComments = false,
91 int iStart = 0)
92 {
93 wxString sTest = sSearch;
94 sTest.MakeLower();
95 for(size_t i = iStart; i < GetLineCount(); i++)
96 {
97 wxString sLine = GetLine(i);
98 if(bIncludeComments || ! sLine.StartsWith(wxT("#")))
99 {
100 if(sLine.StartsWith(sTest))
101 return (int)i;
102 }
103 }
104 return wxNOT_FOUND;
105 }
106
107 bool CommentLine(int nIndex)
108 {
109 if (nIndex < 0)
110 return false;
111 if (nIndex >= (int)GetLineCount() )
112 return false;
113
114 GetLine(nIndex) = GetLine(nIndex).Prepend(wxT("#"));
115 return true;
116 }
117
118 bool CommentLine(const wxString & sTest)
119 {
120 int nIndex = pIndexOf(sTest);
121 if (nIndex < 0)
122 return false;
123 if (nIndex >= (int)GetLineCount() )
124 return false;
125
126 GetLine(nIndex) = GetLine(nIndex).Prepend(wxT("#"));
127 return true;
128 }
129
130 wxString GetVerb(size_t i)
131 {
132 if (i > GetLineCount() )
133 return wxEmptyString;
134
135 wxString sTmp = GetLine(i).BeforeFirst(wxT('='));
136 return sTmp;
137 }
138
139 wxString GetCmd(size_t i)
140 {
141 if (i > GetLineCount() )
142 return wxEmptyString;
143
144 wxString sTmp = GetLine(i).AfterFirst(wxT('='));
145 return sTmp;
146 }
147
148 private:
149 wxArrayString m_text;
150 wxString m_fname;
151 };
152
153 // ----------------------------------------------------------------------------
154 // constants
155 // ----------------------------------------------------------------------------
156
157 // MIME code tracing mask
158 #define TRACE_MIME wxT("mime")
159
160 // give trace messages about the results of mailcap tests
161 #define TRACE_MIME_TEST wxT("mimetest")
162
163 // ----------------------------------------------------------------------------
164
165 // ----------------------------------------------------------------------------
166 // KDE
167 // ----------------------------------------------------------------------------
168
169
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
172 //
173 // 1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
174 // 2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
175 //
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"
179
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??)
182
183 bool wxMimeTypesManagerImpl::CheckKDEDirsExist( const wxString &sOK, const wxString &sTest )
184 {
185 if (sTest.empty())
186 {
187 return wxDir::Exists(sOK);
188 }
189 else
190 {
191 wxString sStart = sOK + wxT("/") + sTest.BeforeFirst(wxT('/'));
192 if (!wxDir::Exists(sStart))
193 wxMkdir(sStart);
194 wxString sEnd = sTest.AfterFirst(wxT('/'));
195 return CheckKDEDirsExist(sStart, sEnd);
196 }
197 }
198
199 bool wxMimeTypesManagerImpl::WriteKDEMimeFile(int index, bool delete_index)
200 {
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");
207
208 bool bTemp;
209 bool bMimeExists = mimeoutfile.Open(sTmp);
210 if (!bMimeExists)
211 {
212 bTemp = mimeoutfile.Create(sTmp);
213 // some unknown error eg out of disk space
214 if (!bTemp)
215 return false;
216 }
217
218 sTmp = wxT(".kde/share/applnk/");
219 CheckKDEDirsExist(sHome, sTmp + sMime.AfterFirst(wxT('/')) );
220 sTmp = sHome + wxT('/') + sTmp + sMime.AfterFirst(wxT('/')) + wxT(".kdelnk");
221
222 bool bAppExists;
223 bAppExists = appoutfile.Open(sTmp);
224 if (!bAppExists)
225 {
226 bTemp = appoutfile.Create(sTmp);
227 // some unknown error eg out of disk space
228 if (!bTemp)
229 return false;
230 }
231
232 // fixed data; write if new file
233 if (!bMimeExists)
234 {
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);
240 }
241
242 if (!bAppExists)
243 {
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(';'));
249 }
250
251 // variable data
252 // ignore locale
253 mimeoutfile.CommentLine(wxT("Comment="));
254 if (!delete_index)
255 mimeoutfile.AddLine(wxT("Comment=") + m_aDescriptions[index]);
256 appoutfile.CommentLine(wxT("Name="));
257 if (!delete_index)
258 appoutfile.AddLine(wxT("Comment=") + m_aDescriptions[index]);
259
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=") );
264 if (!delete_index)
265 mimeoutfile.AddLine(wxT("Icon=") + sTmp );
266 appoutfile.CommentLine(wxT("Icon=") );
267 if (!delete_index)
268 appoutfile.AddLine(wxT("Icon=") + sTmp );
269
270 sTmp = wxT(" ") + m_aExtensions[index];
271
272 wxStringTokenizer tokenizer(sTmp, wxT(" "));
273 sTmp = wxT("Patterns=");
274 mimeoutfile.CommentLine(sTmp);
275 while ( tokenizer.HasMoreTokens() )
276 {
277 // holds an extension; need to change it to *.ext;
278 wxString e = wxT("*.") + tokenizer.GetNextToken() + wxT(";");
279 sTmp += e;
280 }
281
282 if (!delete_index)
283 mimeoutfile.AddLine(sTmp);
284
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") );
289
290 mimeoutfile.CommentLine(wxT("DefaultApp=") );
291 if (!delete_index)
292 mimeoutfile.AddLine(wxT("DefaultApp=") + sTmp);
293
294 sTmp.Replace( wxT("%f"), wxT("") );
295 appoutfile.CommentLine(wxT("Exec="));
296 if (!delete_index)
297 appoutfile.AddLine(wxT("Exec=") + sTmp);
298
299 if (entries->GetCount() > 1)
300 {
301 //other actions as well as open
302 }
303
304 bTemp = false;
305 if (mimeoutfile.Write())
306 bTemp = true;
307 mimeoutfile.Close();
308 if (appoutfile.Write())
309 bTemp = true;
310 appoutfile.Close();
311
312 return bTemp;
313 }
314
315 // Read a KDE .desktop file of type 'Application'
316 void wxMimeTypesManagerImpl::LoadKDEApp(const wxString& filename)
317 {
318 wxLogTrace(TRACE_MIME, wxT("loading KDE file %s"), filename.c_str());
319
320 wxMimeTextFile file;
321 if ( !file.Open(filename) )
322 return;
323
324 // Here, only type 'Application' should be considered.
325 int nIndex = file.pIndexOf( "Type=" );
326 if (nIndex != wxNOT_FOUND && file.GetCmd(nIndex) != "application")
327 return;
328
329 // The hidden entry specifies a file to be ignored.
330 nIndex = file.pIndexOf( "Hidden=" );
331 if (nIndex != wxNOT_FOUND && file.GetCmd(nIndex) == "true")
332 return;
333
334 // Semicolon separated list of mime types handled by the application.
335 nIndex = file.pIndexOf( wxT("MimeType=") );
336 if (nIndex == wxNOT_FOUND)
337 return;
338 wxString mimetypes = file.GetCmd (nIndex);
339
340 // Name of the application
341 wxString nameapp;
342 nIndex = wxNOT_FOUND;
343 #if wxUSE_INTL // try "Name[locale name]" first
344 wxLocale *locale = wxGetLocale();
345 if ( locale )
346 nIndex = file.pIndexOf(_T("Name[")+locale->GetName()+_T("]="));
347 #endif // wxUSE_INTL
348 if(nIndex == wxNOT_FOUND)
349 nIndex = file.pIndexOf( wxT("Name=") );
350 if(nIndex != wxNOT_FOUND)
351 nameapp = file.GetCmd(nIndex);
352
353 // Icon of the application.
354 wxString nameicon, namemini;
355 nIndex = wxNOT_FOUND;
356 #if wxUSE_INTL // try "Icon[locale name]" first
357 if ( locale )
358 nIndex = file.pIndexOf(_T("Icon[")+locale->GetName()+_T("]="));
359 #endif // wxUSE_INTL
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);
365 }
366
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)
371 return;
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);
382
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);
390 }
391 }
392 }
393
394 void wxMimeTypesManagerImpl::LoadKDEAppsFilesFromDir(const wxString& dirname)
395 {
396 // Don't complain if we don't have permissions to read - it confuses users
397 wxLogNull logNull;
398
399 if(! wxDir::Exists(dirname))
400 return;
401 wxDir dir(dirname);
402 if ( !dir.IsOpened() )
403 return;
404
405 wxString filename;
406 // Look into .desktop files
407 bool cont = dir.GetFirst(&filename, _T("*.desktop"), wxDIR_FILES);
408 while (cont)
409 {
410 wxFileName p(dirname, filename);
411 LoadKDEApp( p.GetFullPath() );
412 cont = dir.GetNext(&filename);
413 }
414
415 return;
416
417 // Look recursively into subdirs
418 cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS);
419 while (cont)
420 {
421 wxFileName p(dirname, wxEmptyString);
422 p.AppendDir(filename);
423 LoadKDEAppsFilesFromDir( p.GetPath() );
424 cont = dir.GetNext(&filename);
425 }
426 }
427
428
429 // ----------------------------------------------------------------------------
430 // wxFileTypeImpl (Unix)
431 // ----------------------------------------------------------------------------
432
433 wxString wxFileTypeImpl::GetExpandedCommand(const wxString & verb, const wxFileType::MessageParameters& params) const
434 {
435 wxString sTmp;
436 size_t i = 0;
437 while ( (i < m_index.GetCount() ) && sTmp.empty() )
438 {
439 sTmp = m_manager->GetCommand( verb, m_index[i] );
440 i++;
441 }
442
443 return wxFileType::ExpandCommand(sTmp, params);
444 }
445
446 bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
447 {
448 wxString sTmp;
449 size_t i = 0;
450 while ( (i < m_index.GetCount() ) && sTmp.empty() )
451 {
452 sTmp = m_manager->m_aIcons[m_index[i]];
453 i++;
454 }
455
456 if ( sTmp.empty() )
457 return false;
458
459 if ( iconLoc )
460 {
461 iconLoc->SetFileName(sTmp);
462 }
463
464 return true;
465 }
466
467 bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
468 {
469 mimeTypes.Clear();
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]]);
473
474 return true;
475 }
476
477 size_t wxFileTypeImpl::GetAllCommands(wxArrayString *verbs,
478 wxArrayString *commands,
479 const wxFileType::MessageParameters& params) const
480 {
481 wxString vrb, cmd, sTmp;
482 size_t count = 0;
483 wxMimeTypeCommands * sPairs;
484
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++)
488 {
489 // list of verb = command pairs for this mimetype
490 sPairs = m_manager->m_aEntries [m_index[n]];
491 size_t i;
492 for ( i = 0; i < sPairs->GetCount(); i++ )
493 {
494 vrb = sPairs->GetVerb(i);
495 // some gnome entries have "." inside
496 vrb = vrb.AfterLast(wxT('.'));
497 cmd = sPairs->GetCmd(i);
498 if (! cmd.empty() )
499 {
500 cmd = wxFileType::ExpandCommand(cmd, params);
501 count++;
502 if ( vrb.IsSameAs(wxT("open")))
503 {
504 if ( verbs )
505 verbs->Insert(vrb, 0u);
506 if ( commands )
507 commands ->Insert(cmd, 0u);
508 }
509 else
510 {
511 if ( verbs )
512 verbs->Add(vrb);
513 if ( commands )
514 commands->Add(cmd);
515 }
516 }
517 }
518 }
519
520 return count;
521 }
522
523 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
524 {
525 const wxString strExtensions = m_manager->GetExtension(m_index[0]);
526 extensions.Empty();
527
528 // one extension in the space or comma-delimited list
529 wxString strExt;
530 wxString::const_iterator end = strExtensions.end();
531 for ( wxString::const_iterator p = strExtensions.begin(); /* nothing */; ++p )
532 {
533 if ( p == end || *p == wxT(' ') || *p == wxT(',') )
534 {
535 if ( !strExt.empty() )
536 {
537 extensions.Add(strExt);
538 strExt.Empty();
539 }
540 //else: repeated spaces
541 // (shouldn't happen, but it's not that important if it does happen)
542
543 if ( p == end )
544 break;
545 }
546 else if ( *p == wxT('.') )
547 {
548 // remove the dot from extension (but only if it's the first char)
549 if ( !strExt.empty() )
550 {
551 strExt += wxT('.');
552 }
553 //else: no, don't append it
554 }
555 else
556 {
557 strExt += *p;
558 }
559 }
560
561 return true;
562 }
563
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
568 bool
569 wxFileTypeImpl::SetCommand(const wxString& cmd,
570 const wxString& verb,
571 bool WXUNUSED(overwriteprompt))
572 {
573 wxArrayString strExtensions;
574 wxString strDesc, strIcon;
575
576 wxArrayString strTypes;
577 GetMimeTypes(strTypes);
578 if ( strTypes.IsEmpty() )
579 return false;
580
581 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
582 entry->Add(verb + wxT("=") + cmd + wxT(" %s "));
583
584 bool ok = false;
585 size_t nCount = strTypes.GetCount();
586 for ( size_t i = 0; i < nCount; i++ )
587 {
588 if ( m_manager->DoAssociation
589 (
590 strTypes[i],
591 strIcon,
592 entry,
593 strExtensions,
594 strDesc
595 ) )
596 {
597 // DoAssociation() took ownership of entry, don't delete it below
598 ok = true;
599 }
600 }
601
602 if ( !ok )
603 delete entry;
604
605 return ok;
606 }
607
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))
610 {
611 if (strIcon.empty())
612 return false;
613
614 wxArrayString strExtensions;
615 wxString strDesc;
616
617 wxArrayString strTypes;
618 GetMimeTypes(strTypes);
619 if ( strTypes.IsEmpty() )
620 return false;
621
622 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
623 bool ok = false;
624 size_t nCount = strTypes.GetCount();
625 for ( size_t i = 0; i < nCount; i++ )
626 {
627 if ( m_manager->DoAssociation
628 (
629 strTypes[i],
630 strIcon,
631 entry,
632 strExtensions,
633 strDesc
634 ) )
635 {
636 // we don't need to free entry now, DoAssociation() took ownership
637 // of it
638 ok = true;
639 }
640 }
641
642 if ( !ok )
643 delete entry;
644
645 return ok;
646 }
647
648 // ----------------------------------------------------------------------------
649 // wxMimeTypesManagerImpl (Unix)
650 // ----------------------------------------------------------------------------
651
652 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
653 {
654 m_initialized = false;
655 }
656
657 void wxMimeTypesManagerImpl::InitIfNeeded()
658 {
659 if ( !m_initialized )
660 {
661 // set the flag first to prevent recursion
662 m_initialized = true;
663
664 wxString wm = wxTheApp->GetTraits()->GetDesktopEnvironment();
665
666 if (wm == wxT("KDE"))
667 Initialize( wxMAILCAP_KDE );
668 else if (wm == wxT("GNOME"))
669 Initialize( wxMAILCAP_GNOME );
670 else
671 Initialize();
672 }
673 }
674
675 // read system and user mailcaps and other files
676 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles,
677 const wxString& sExtraDir)
678 {
679 #ifdef __VMS
680 // XDG tables are never installed on OpenVMS
681 return;
682 #endif
683
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
687 // heuristic.
688 {
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");
695 wxArrayString dirs;
696
697 wxStringTokenizer tokenizer(xdgDataDirs, wxT(":"));
698 while ( tokenizer.HasMoreTokens() )
699 {
700 wxString p = tokenizer.GetNextToken();
701 dirs.Add(p);
702 }
703 dirs.insert(dirs.begin(), xdgDataHome);
704
705 wxString defaultsList;
706 size_t i;
707 for (i = 0; i < dirs.GetCount(); i++)
708 {
709 wxString f = dirs[i];
710 if (f.Last() != '/') f += '/';
711 f += "applications/defaults.list";
712 if (wxFileExists(f))
713 {
714 defaultsList = f;
715 break;
716 }
717 }
718
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++)
722 {
723 wxString dirStr = dirs[nDir];
724 if (dirStr.Last() != '/') dirStr += '/';
725 dirStr += "applications";
726 LoadKDEAppsFilesFromDir(dirStr);
727 }
728
729 if (!defaultsList.IsEmpty())
730 {
731 wxArrayString deskTopFilesSeen;
732
733 wxMimeTextFile textfile(defaultsList);
734 if ( textfile.Open() )
735 {
736 int nIndex = textfile.pIndexOf( wxT("[Default Applications]") );
737 if (nIndex != wxNOT_FOUND)
738 {
739 for (i = nIndex+1; i < textfile.GetLineCount(); i++)
740 {
741 if (textfile.GetLine(i).Find(wxT("=")) != wxNOT_FOUND)
742 {
743 wxString mimeType = textfile.GetVerb(i);
744 wxString desktopFile = textfile.GetCmd(i);
745
746 if (deskTopFilesSeen.Index(desktopFile) == wxNOT_FOUND)
747 {
748 deskTopFilesSeen.Add(desktopFile);
749 size_t j;
750 for (j = 0; j < dirs.GetCount(); j++)
751 {
752 wxString desktopPath = dirs[j];
753 if (desktopPath.Last() == '/')
754 desktopPath += "applications/";
755 else
756 desktopPath += "/applications/";
757 desktopPath += desktopFile;
758
759 if (wxFileExists(desktopPath))
760 LoadKDEApp(desktopPath);
761 }
762 }
763 }
764 }
765 }
766 }
767 }
768 }
769 }
770
771 // clear data so you can read another group of WM files
772 void wxMimeTypesManagerImpl::ClearData()
773 {
774 m_aTypes.Clear();
775 m_aIcons.Clear();
776 m_aExtensions.Clear();
777 m_aDescriptions.Clear();
778
779 WX_CLEAR_ARRAY(m_aEntries);
780 m_aEntries.Empty();
781 }
782
783 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl()
784 {
785 ClearData();
786 }
787
788 wxFileType * wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
789 {
790 InitIfNeeded();
791
792 wxString strType = ftInfo.GetMimeType();
793 wxString strDesc = ftInfo.GetDescription();
794 wxString strIcon = ftInfo.GetIconFile();
795
796 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
797
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 "));
802
803 // now find where these extensions are in the data store and remove them
804 wxArrayString sA_Exts = ftInfo.GetExtensions();
805 wxString sExt, sExtStore;
806 size_t i, nIndex;
807 size_t nExtCount = sA_Exts.GetCount();
808 for (i=0; i < nExtCount; i++)
809 {
810 sExt = sA_Exts.Item(i);
811
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++)
817 {
818 sExtStore = m_aExtensions.Item(nIndex);
819 if (sExtStore.Replace(sExt, wxT(" ") ) > 0)
820 m_aExtensions.Item(nIndex) = sExtStore;
821 }
822 }
823
824 if ( !DoAssociation(strType, strIcon, entry, sA_Exts, strDesc) )
825 return NULL;
826
827 return GetFileTypeFromMimeType(strType);
828 }
829
830 bool wxMimeTypesManagerImpl::DoAssociation(const wxString& strType,
831 const wxString& strIcon,
832 wxMimeTypeCommands *entry,
833 const wxArrayString& strExtensions,
834 const wxString& strDesc)
835 {
836 int nIndex = AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, true);
837
838 if ( nIndex == wxNOT_FOUND )
839 return false;
840
841 return WriteMimeInfo(nIndex, false);
842 }
843
844 bool wxMimeTypesManagerImpl::WriteMimeInfo(int nIndex, bool delete_mime )
845 {
846 bool ok = true;
847
848 // Don't write GNOME files here as this is not
849 // allowed and simply doesn't work
850
851 // if (m_mailcapStylesInited & wxMAILCAP_KDE)
852 {
853 // write in KDE format;
854 if (WriteKDEMimeFile(nIndex, delete_mime) )
855 ok = false;
856 }
857
858 return ok;
859 }
860
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)
867 {
868 InitIfNeeded();
869
870 // ensure mimetype is always lower case
871 wxString mimeType = strType.Lower();
872
873 // is this a known MIME type?
874 int nIndex = m_aTypes.Index(mimeType);
875 if ( nIndex == wxNOT_FOUND )
876 {
877 // new file type
878 m_aTypes.Add(mimeType);
879 m_aIcons.Add(strIcon);
880 m_aEntries.Add(entry ? entry : new wxMimeTypeCommands);
881
882 // change nIndex so we can use it below to add the extensions
883 m_aExtensions.Add(wxEmptyString);
884 nIndex = m_aExtensions.size() - 1;
885
886 m_aDescriptions.Add(strDesc);
887 }
888 else // yes, we already have it
889 {
890 if ( replaceExisting )
891 {
892 // if new description change it
893 if ( !strDesc.empty())
894 m_aDescriptions[nIndex] = strDesc;
895
896 // if new icon change it
897 if ( !strIcon.empty())
898 m_aIcons[nIndex] = strIcon;
899
900 if ( entry )
901 {
902 delete m_aEntries[nIndex];
903 m_aEntries[nIndex] = entry;
904 }
905 }
906 else // add data we don't already have ...
907 {
908 // if new description add only if none
909 if ( m_aDescriptions[nIndex].empty() )
910 m_aDescriptions[nIndex] = strDesc;
911
912 // if new icon and no existing icon
913 if ( m_aIcons[nIndex].empty() )
914 m_aIcons[nIndex] = strIcon;
915
916 // add any new entries...
917 if ( entry )
918 {
919 wxMimeTypeCommands *entryOld = m_aEntries[nIndex];
920
921 size_t count = entry->GetCount();
922 for ( size_t i = 0; i < count; i++ )
923 {
924 const wxString& verb = entry->GetVerb(i);
925 if ( !entryOld->HasVerb(verb) )
926 {
927 entryOld->AddOrReplaceVerb(verb, entry->GetCmd(i));
928 }
929 }
930
931 // as we don't store it anywhere, it won't be deleted later as
932 // usual -- do it immediately instead
933 delete entry;
934 }
935 }
936 }
937
938 // always add the extensions to this mimetype
939 wxString& exts = m_aExtensions[nIndex];
940
941 // add all extensions we don't have yet
942 wxString ext;
943 size_t count = strExtensions.GetCount();
944 for ( size_t i = 0; i < count; i++ )
945 {
946 ext = strExtensions[i];
947 ext += wxT(' ');
948
949 if ( exts.Find(ext) == wxNOT_FOUND )
950 {
951 exts += ext;
952 }
953 }
954
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() );
960
961 return nIndex;
962 }
963
964 wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
965 {
966 if (ext.empty() )
967 return NULL;
968
969 InitIfNeeded();
970
971 size_t count = m_aExtensions.GetCount();
972 for ( size_t n = 0; n < count; n++ )
973 {
974 wxStringTokenizer tk(m_aExtensions[n], wxT(' '));
975
976 while ( tk.HasMoreTokens() )
977 {
978 // consider extensions as not being case-sensitive
979 if ( tk.GetNextToken().IsSameAs(ext, false /* no case */) )
980 {
981 // found
982 wxFileType *fileType = new wxFileType;
983 fileType->m_impl->Init(this, n);
984
985 return fileType;
986 }
987 }
988 }
989
990 return NULL;
991 }
992
993 wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
994 {
995 InitIfNeeded();
996
997 wxFileType * fileType = NULL;
998 // mime types are not case-sensitive
999 wxString mimetype(mimeType);
1000 mimetype.MakeLower();
1001
1002 // first look for an exact match
1003 int index = m_aTypes.Index(mimetype);
1004 if ( index != wxNOT_FOUND )
1005 {
1006 fileType = new wxFileType;
1007 fileType->m_impl->Init(this, index);
1008 }
1009
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.
1013
1014 index = wxNOT_FOUND;
1015 wxString strCategory = mimetype.BeforeFirst(wxT('/'));
1016
1017 size_t nCount = m_aTypes.GetCount();
1018 for ( size_t n = 0; n < nCount; n++ )
1019 {
1020 if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
1021 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") )
1022 {
1023 index = n;
1024 break;
1025 }
1026 }
1027
1028 if ( index != wxNOT_FOUND )
1029 {
1030 // don't throw away fileType that was already found
1031 if (!fileType)
1032 fileType = new wxFileType;
1033 fileType->m_impl->Init(this, index);
1034 }
1035
1036 return fileType;
1037 }
1038
1039 wxString wxMimeTypesManagerImpl::GetCommand(const wxString & verb, size_t nIndex) const
1040 {
1041 wxString command, testcmd, sV, sTmp;
1042 sV = verb + wxT("=");
1043
1044 // list of verb = command pairs for this mimetype
1045 wxMimeTypeCommands * sPairs = m_aEntries [nIndex];
1046
1047 size_t i;
1048 size_t nCount = sPairs->GetCount();
1049 for ( i = 0; i < nCount; i++ )
1050 {
1051 sTmp = sPairs->GetVerbCmd (i);
1052 if ( sTmp.Contains(sV) )
1053 command = sTmp.AfterFirst(wxT('='));
1054 }
1055
1056 return command;
1057 }
1058
1059 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
1060 {
1061 InitIfNeeded();
1062
1063 wxString extensions;
1064 const wxArrayString& exts = filetype.GetExtensions();
1065 size_t nExts = exts.GetCount();
1066 for ( size_t nExt = 0; nExt < nExts; nExt++ )
1067 {
1068 if ( nExt > 0 )
1069 extensions += wxT(' ');
1070
1071 extensions += exts[nExt];
1072 }
1073
1074 AddMimeTypeInfo(filetype.GetMimeType(),
1075 extensions,
1076 filetype.GetDescription());
1077 }
1078
1079 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
1080 const wxString& strExtensions,
1081 const wxString& strDesc)
1082 {
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
1086 wxString strIcon;
1087 wxString sTmp = strExtensions;
1088
1089 wxArrayString sExts;
1090 sTmp.Trim().Trim(false);
1091
1092 while (!sTmp.empty())
1093 {
1094 sExts.Add(sTmp.AfterLast(wxT(' ')));
1095 sTmp = sTmp.BeforeLast(wxT(' '));
1096 }
1097
1098 AddToMimeData(strMimeType, strIcon, NULL, sExts, strDesc, true);
1099 }
1100
1101 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
1102 {
1103 InitIfNeeded();
1104
1105 mimetypes.Empty();
1106
1107 size_t count = m_aTypes.GetCount();
1108 for ( size_t n = 0; n < count; n++ )
1109 {
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 )
1113 {
1114 mimetypes.Add(type);
1115 }
1116 }
1117
1118 return mimetypes.GetCount();
1119 }
1120
1121 // ----------------------------------------------------------------------------
1122 // writing to MIME type files
1123 // ----------------------------------------------------------------------------
1124
1125 bool wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
1126 {
1127 InitIfNeeded();
1128
1129 wxArrayString sMimeTypes;
1130 ft->GetMimeTypes(sMimeTypes);
1131
1132 size_t i;
1133 size_t nCount = sMimeTypes.GetCount();
1134 for (i = 0; i < nCount; i ++)
1135 {
1136 const wxString &sMime = sMimeTypes.Item(i);
1137 int nIndex = m_aTypes.Index(sMime);
1138 if ( nIndex == wxNOT_FOUND)
1139 {
1140 // error if we get here ??
1141 return false;
1142 }
1143 else
1144 {
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);
1151 }
1152 }
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() );
1158
1159 return true;
1160 }
1161
1162 #endif
1163 // wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE