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