Document domain parameter of wxTranslations::GetTranslatedString().
[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 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // Licence: wxWindows licence (part of wxExtra library)
9 /////////////////////////////////////////////////////////////////////////////
10
11 // for compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_MIMETYPE && wxUSE_FILE
19
20 #include "wx/unix/mimetype.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/dynarray.h"
24 #include "wx/string.h"
25 #include "wx/intl.h"
26 #include "wx/log.h"
27 #include "wx/utils.h"
28 #endif
29
30 #include "wx/file.h"
31 #include "wx/confbase.h"
32
33 #include "wx/ffile.h"
34 #include "wx/dir.h"
35 #include "wx/tokenzr.h"
36 #include "wx/iconloc.h"
37 #include "wx/filename.h"
38 #include "wx/app.h"
39 #include "wx/apptrait.h"
40
41 // other standard headers
42 #include <ctype.h>
43
44 class wxMimeTextFile
45 {
46 public:
47 wxMimeTextFile()
48 {
49 }
50
51 wxMimeTextFile(const wxString& fname)
52 {
53 m_fname = fname;
54 }
55
56 bool Open()
57 {
58 wxFFile file( m_fname );
59 if (!file.IsOpened())
60 return false;
61
62 size_t size = file.Length();
63 wxCharBuffer buffer( size );
64 file.Read( (void*) (const char*) buffer, size );
65
66 // Check for valid UTF-8 here?
67 wxString all = wxString::FromUTF8( buffer, size );
68
69 wxStringTokenizer tok( all, "\n" );
70 while (tok.HasMoreTokens())
71 {
72 wxString t = tok.GetNextToken();
73 t.MakeLower();
74 if ((!!t) && (t.Find( "comment" ) != 0) && (t.Find( "#" ) != 0) && (t.Find( "generic" ) != 0))
75 m_text.Add( t );
76 }
77 return true;
78 }
79
80 unsigned int GetLineCount() const { return m_text.GetCount(); }
81 wxString &GetLine( unsigned int line ) { return m_text[line]; }
82
83 int pIndexOf(const wxString& sSearch,
84 bool bIncludeComments = false,
85 int iStart = 0)
86 {
87 wxString sTest = sSearch;
88 sTest.MakeLower();
89 for(size_t i = iStart; i < GetLineCount(); i++)
90 {
91 wxString sLine = GetLine(i);
92 if(bIncludeComments || ! sLine.StartsWith(wxT("#")))
93 {
94 if(sLine.StartsWith(sTest))
95 return (int)i;
96 }
97 }
98 return wxNOT_FOUND;
99 }
100
101 wxString GetVerb(size_t i)
102 {
103 if (i > GetLineCount() )
104 return wxEmptyString;
105
106 wxString sTmp = GetLine(i).BeforeFirst(wxT('='));
107 return sTmp;
108 }
109
110 wxString GetCmd(size_t i)
111 {
112 if (i > GetLineCount() )
113 return wxEmptyString;
114
115 wxString sTmp = GetLine(i).AfterFirst(wxT('='));
116 return sTmp;
117 }
118
119 private:
120 wxArrayString m_text;
121 wxString m_fname;
122 };
123
124 // ----------------------------------------------------------------------------
125 // constants
126 // ----------------------------------------------------------------------------
127
128 // MIME code tracing mask
129 #define TRACE_MIME wxT("mime")
130
131
132 // Read a XDG *.desktop file of type 'Application'
133 void wxMimeTypesManagerImpl::LoadXDGApp(const wxString& filename)
134 {
135 wxLogTrace(TRACE_MIME, wxT("loading XDG file %s"), filename.c_str());
136
137 wxMimeTextFile file(filename);
138 if ( !file.Open() )
139 return;
140
141 // Here, only type 'Application' should be considered.
142 int nIndex = file.pIndexOf( "Type=" );
143 if (nIndex != wxNOT_FOUND && file.GetCmd(nIndex) != "application")
144 return;
145
146 // The hidden entry specifies a file to be ignored.
147 nIndex = file.pIndexOf( "Hidden=" );
148 if (nIndex != wxNOT_FOUND && file.GetCmd(nIndex) == "true")
149 return;
150
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);
156
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 )
163 nIndex = file.pIndexOf(wxT("Name[")+locale->GetName()+wxT("]="));
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 )
175 nIndex = file.pIndexOf(wxT("Icon[")+locale->GetName()+wxT("]="));
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
200 wxStringTokenizer tokenizer(mimetypes, wxT(";"));
201 while(tokenizer.HasMoreTokens()) {
202 wxString mimetype = tokenizer.GetNextToken().Lower();
203 nIndex = m_aTypes.Index(mimetype);
204 if(nIndex != wxNOT_FOUND) { // is this a known MIME type?
205 wxMimeTypeCommands* entry = m_aEntries[nIndex];
206 entry->AddOrReplaceVerb(wxT("open"), sCmd);
207 }
208 }
209 }
210
211 void wxMimeTypesManagerImpl::LoadXDGAppsFilesFromDir(const wxString& dirname)
212 {
213 // Don't complain if we don't have permissions to read - it confuses users
214 wxLogNull logNull;
215
216 if(! wxDir::Exists(dirname))
217 return;
218 wxDir dir(dirname);
219 if ( !dir.IsOpened() )
220 return;
221
222 wxString filename;
223 // Look into .desktop files
224 bool cont = dir.GetFirst(&filename, wxT("*.desktop"), wxDIR_FILES);
225 while (cont)
226 {
227 wxFileName p(dirname, filename);
228 LoadXDGApp( p.GetFullPath() );
229 cont = dir.GetNext(&filename);
230 }
231
232 #if 0
233 // RR: I'm not sure this makes any sense. On my system we'll just
234 // scan the YAST2 and other useless directories
235
236 // Look recursively into subdirs
237 cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS);
238 while (cont)
239 {
240 wxFileName p(dirname, wxEmptyString);
241 p.AppendDir(filename);
242 LoadXDGAppsFilesFromDir( p.GetPath() );
243 cont = dir.GetNext(&filename);
244 }
245 #endif
246 }
247
248
249 void wxMimeTypesManagerImpl::LoadXDGGlobs(const wxString& filename)
250 {
251 if ( !wxFileName::FileExists(filename) )
252 return;
253
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
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 );
269
270 AddToMimeData(mime, wxEmptyString, NULL, exts, wxEmptyString, true );
271 }
272 }
273
274 // ----------------------------------------------------------------------------
275 // wxFileTypeImpl (Unix)
276 // ----------------------------------------------------------------------------
277
278 wxString wxFileTypeImpl::GetExpandedCommand(const wxString & verb, const wxFileType::MessageParameters& params) const
279 {
280 wxString sTmp;
281 size_t i = 0;
282 while ( (i < m_index.GetCount() ) && sTmp.empty() )
283 {
284 sTmp = m_manager->GetCommand( verb, m_index[i] );
285 i++;
286 }
287
288 return wxFileType::ExpandCommand(sTmp, params);
289 }
290
291 bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
292 {
293 wxString sTmp;
294 size_t i = 0;
295 while ( (i < m_index.GetCount() ) && sTmp.empty() )
296 {
297 sTmp = m_manager->m_aIcons[m_index[i]];
298 i++;
299 }
300
301 if ( sTmp.empty() )
302 return false;
303
304 if ( iconLoc )
305 {
306 iconLoc->SetFileName(sTmp);
307 }
308
309 return true;
310 }
311
312 bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
313 {
314 mimeTypes.Clear();
315 size_t nCount = m_index.GetCount();
316 for (size_t i = 0; i < nCount; i++)
317 mimeTypes.Add(m_manager->m_aTypes[m_index[i]]);
318
319 return true;
320 }
321
322 size_t wxFileTypeImpl::GetAllCommands(wxArrayString *verbs,
323 wxArrayString *commands,
324 const wxFileType::MessageParameters& params) const
325 {
326 wxString vrb, cmd, sTmp;
327 size_t count = 0;
328 wxMimeTypeCommands * sPairs;
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
332 for (size_t n = 0; ((count == 0) && (n < m_index.GetCount())); n++)
333 {
334 // list of verb = command pairs for this mimetype
335 sPairs = m_manager->m_aEntries [m_index[n]];
336 size_t i;
337 for ( i = 0; i < sPairs->GetCount(); i++ )
338 {
339 vrb = sPairs->GetVerb(i);
340 // some gnome entries have "." inside
341 vrb = vrb.AfterLast(wxT('.'));
342 cmd = sPairs->GetCmd(i);
343 if (! cmd.empty() )
344 {
345 cmd = wxFileType::ExpandCommand(cmd, params);
346 count++;
347 if ( vrb.IsSameAs(wxT("open")))
348 {
349 if ( verbs )
350 verbs->Insert(vrb, 0u);
351 if ( commands )
352 commands ->Insert(cmd, 0u);
353 }
354 else
355 {
356 if ( verbs )
357 verbs->Add(vrb);
358 if ( commands )
359 commands->Add(cmd);
360 }
361 }
362 }
363 }
364
365 return count;
366 }
367
368 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
369 {
370 const wxString strExtensions = m_manager->GetExtension(m_index[0]);
371 extensions.Empty();
372
373 // one extension in the space or comma-delimited list
374 wxString strExt;
375 wxString::const_iterator end = strExtensions.end();
376 for ( wxString::const_iterator p = strExtensions.begin(); /* nothing */; ++p )
377 {
378 if ( p == end || *p == wxT(' ') || *p == wxT(',') )
379 {
380 if ( !strExt.empty() )
381 {
382 extensions.Add(strExt);
383 strExt.Empty();
384 }
385 //else: repeated spaces
386 // (shouldn't happen, but it's not that important if it does happen)
387
388 if ( p == end )
389 break;
390 }
391 else if ( *p == wxT('.') )
392 {
393 // remove the dot from extension (but only if it's the first char)
394 if ( !strExt.empty() )
395 {
396 strExt += wxT('.');
397 }
398 //else: no, don't append it
399 }
400 else
401 {
402 strExt += *p;
403 }
404 }
405
406 return true;
407 }
408
409 // set an arbitrary command:
410 // could adjust the code to ask confirmation if it already exists and
411 // overwriteprompt is true, but this is currently ignored as *Associate* has
412 // no overwrite prompt
413 bool
414 wxFileTypeImpl::SetCommand(const wxString& cmd,
415 const wxString& verb,
416 bool WXUNUSED(overwriteprompt))
417 {
418 wxArrayString strExtensions;
419 wxString strDesc, strIcon;
420
421 wxArrayString strTypes;
422 GetMimeTypes(strTypes);
423 if ( strTypes.IsEmpty() )
424 return false;
425
426 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
427 entry->Add(verb + wxT("=") + cmd + wxT(" %s "));
428
429 bool ok = false;
430 size_t nCount = strTypes.GetCount();
431 for ( size_t i = 0; i < nCount; i++ )
432 {
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 }
445 }
446
447 if ( !ok )
448 delete entry;
449
450 return ok;
451 }
452
453 // ignore index on the grounds that we only have one icon in a Unix file
454 bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int WXUNUSED(index))
455 {
456 if (strIcon.empty())
457 return false;
458
459 wxArrayString strExtensions;
460 wxString strDesc;
461
462 wxArrayString strTypes;
463 GetMimeTypes(strTypes);
464 if ( strTypes.IsEmpty() )
465 return false;
466
467 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
468 bool ok = false;
469 size_t nCount = strTypes.GetCount();
470 for ( size_t i = 0; i < nCount; i++ )
471 {
472 if ( m_manager->DoAssociation
473 (
474 strTypes[i],
475 strIcon,
476 entry,
477 strExtensions,
478 strDesc
479 ) )
480 {
481 // we don't need to free entry now, DoAssociation() took ownership
482 // of it
483 ok = true;
484 }
485 }
486
487 if ( !ok )
488 delete entry;
489
490 return ok;
491 }
492
493 // ----------------------------------------------------------------------------
494 // wxMimeTypesManagerImpl (Unix)
495 // ----------------------------------------------------------------------------
496
497 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
498 {
499 m_initialized = false;
500 }
501
502 void wxMimeTypesManagerImpl::InitIfNeeded()
503 {
504 if ( !m_initialized )
505 {
506 // set the flag first to prevent recursion
507 m_initialized = true;
508
509 int mailcapStyles = wxMAILCAP_ALL;
510 if ( wxAppTraits * const traits = wxApp::GetTraitsIfExists() )
511 {
512 wxString wm = traits->GetDesktopEnvironment();
513
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);
522 }
523 }
524
525
526
527 // read system and user mailcaps and other files
528 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles,
529 const wxString& sExtraDir)
530 {
531 #ifdef __VMS
532 // XDG tables are never installed on OpenVMS
533 return;
534 #else
535
536 // Read MIME type - extension associations
537 LoadXDGGlobs( "/usr/share/mime/globs" );
538 LoadXDGGlobs( "/usr/local/share/mime/globs" );
539
540 // Load desktop files for XDG, and then override them with the defaults.
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.
544 {
545 wxString xdgDataHome = wxGetenv("XDG_DATA_HOME");
546 if ( xdgDataHome.empty() )
547 xdgDataHome = wxGetHomeDir() + "/.local/share";
548 wxString xdgDataDirs = wxGetenv("XDG_DATA_DIRS");
549 if ( xdgDataDirs.empty() )
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 }
562
563 wxArrayString dirs;
564 wxStringTokenizer tokenizer(xdgDataDirs, ":");
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 {
576 wxString f = dirs[i];
577 if (f.Last() != '/') f += '/';
578 f += "applications/defaults.list";
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 {
590 wxString dirStr = dirs[nDir];
591 if (dirStr.Last() != '/') dirStr += '/';
592 dirStr += "applications";
593 LoadXDGAppsFilesFromDir(dirStr);
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 {
608 if (textfile.GetLine(i).Find(wxT("=")) != wxNOT_FOUND)
609 {
610 wxString desktopFile = textfile.GetCmd(i);
611
612 if (deskTopFilesSeen.Index(desktopFile) == wxNOT_FOUND)
613 {
614 deskTopFilesSeen.Add(desktopFile);
615 size_t j;
616 for (j = 0; j < dirs.GetCount(); j++)
617 {
618 wxString desktopPath = dirs[j];
619 if (desktopPath.Last() != '/') desktopPath += '/';
620 desktopPath += "applications/";
621 desktopPath += desktopFile;
622
623 if (wxFileExists(desktopPath))
624 LoadXDGApp(desktopPath);
625 }
626 }
627 }
628 }
629 }
630 }
631 }
632 }
633 #endif
634 }
635
636 // clear data so you can read another group of WM files
637 void 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
648 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl()
649 {
650 ClearData();
651 }
652
653 wxFileType * wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
654 {
655 InitIfNeeded();
656
657 wxString strType = ftInfo.GetMimeType();
658 wxString strDesc = ftInfo.GetDescription();
659 wxString strIcon = ftInfo.GetIconFile();
660
661 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
662
663 if ( ! ftInfo.GetOpenCommand().empty())
664 entry->Add(wxT("open=") + ftInfo.GetOpenCommand() + wxT(" %s "));
665 if ( ! ftInfo.GetPrintCommand().empty())
666 entry->Add(wxT("print=") + ftInfo.GetPrintCommand() + wxT(" %s "));
667
668 // now find where these extensions are in the data store and remove them
669 wxArrayString sA_Exts = ftInfo.GetExtensions();
670 wxString sExt, sExtStore;
671 size_t i, nIndex;
672 size_t nExtCount = sA_Exts.GetCount();
673 for (i=0; i < nExtCount; i++)
674 {
675 sExt = sA_Exts.Item(i);
676
677 // clean up to just a space before and after
678 sExt.Trim().Trim(false);
679 sExt = wxT(' ') + sExt + wxT(' ');
680 size_t nCount = m_aExtensions.GetCount();
681 for (nIndex = 0; nIndex < nCount; nIndex++)
682 {
683 sExtStore = m_aExtensions.Item(nIndex);
684 if (sExtStore.Replace(sExt, wxT(" ") ) > 0)
685 m_aExtensions.Item(nIndex) = sExtStore;
686 }
687 }
688
689 if ( !DoAssociation(strType, strIcon, entry, sA_Exts, strDesc) )
690 return NULL;
691
692 return GetFileTypeFromMimeType(strType);
693 }
694
695 bool wxMimeTypesManagerImpl::DoAssociation(const wxString& strType,
696 const wxString& strIcon,
697 wxMimeTypeCommands *entry,
698 const wxArrayString& strExtensions,
699 const wxString& strDesc)
700 {
701 int nIndex = AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, true);
702
703 if ( nIndex == wxNOT_FOUND )
704 return false;
705
706 return true;
707 }
708
709 int wxMimeTypesManagerImpl::AddToMimeData(const wxString& strType,
710 const wxString& strIcon,
711 wxMimeTypeCommands *entry,
712 const wxArrayString& strExtensions,
713 const wxString& strDesc,
714 bool replaceExisting)
715 {
716 InitIfNeeded();
717
718 // ensure mimetype is always lower case
719 wxString mimeType = strType.Lower();
720
721 // is this a known MIME type?
722 int nIndex = m_aTypes.Index(mimeType);
723 if ( nIndex == wxNOT_FOUND )
724 {
725 // We put MIME types containing "application" at the end, so that
726 // if the MIME type for the extension "htm" is searched for, it will
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);
734
735 // change nIndex so we can use it below to add the extensions
736 m_aExtensions.Add(wxEmptyString);
737 nIndex = m_aExtensions.size() - 1;
738
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 }
754 }
755 else // yes, we already have it
756 {
757 if ( replaceExisting )
758 {
759 // if new description change it
760 if ( !strDesc.empty())
761 m_aDescriptions[nIndex] = strDesc;
762
763 // if new icon change it
764 if ( !strIcon.empty())
765 m_aIcons[nIndex] = strIcon;
766
767 if ( entry )
768 {
769 delete m_aEntries[nIndex];
770 m_aEntries[nIndex] = entry;
771 }
772 }
773 else // add data we don't already have ...
774 {
775 // if new description add only if none
776 if ( m_aDescriptions[nIndex].empty() )
777 m_aDescriptions[nIndex] = strDesc;
778
779 // if new icon and no existing icon
780 if ( m_aIcons[nIndex].empty() )
781 m_aIcons[nIndex] = strIcon;
782
783 // add any new entries...
784 if ( entry )
785 {
786 wxMimeTypeCommands *entryOld = m_aEntries[nIndex];
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 }
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;
801 }
802 }
803 }
804
805 // always add the extensions to this mimetype
806 wxString& exts = m_aExtensions[nIndex];
807
808 // add all extensions we don't have yet
809 wxString ext;
810 size_t count = strExtensions.GetCount();
811 for ( size_t i = 0; i < count; i++ )
812 {
813 ext = strExtensions[i];
814 ext += wxT(' ');
815
816 if ( exts.Find(ext) == wxNOT_FOUND )
817 {
818 exts += ext;
819 }
820 }
821
822 // check data integrity
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() );
827
828 return nIndex;
829 }
830
831 wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
832 {
833 if (ext.empty() )
834 return NULL;
835
836 InitIfNeeded();
837
838 size_t count = m_aExtensions.GetCount();
839 for ( size_t n = 0; n < count; n++ )
840 {
841 wxStringTokenizer tk(m_aExtensions[n], wxT(' '));
842
843 while ( tk.HasMoreTokens() )
844 {
845 // consider extensions as not being case-sensitive
846 if ( tk.GetNextToken().IsSameAs(ext, false /* no case */) )
847 {
848 // found
849 wxFileType *fileType = new wxFileType;
850 fileType->m_impl->Init(this, n);
851
852 return fileType;
853 }
854 }
855 }
856
857 return NULL;
858 }
859
860 wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
861 {
862 InitIfNeeded();
863
864 wxFileType * fileType = NULL;
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);
871
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
885 size_t nCount = m_aTypes.GetCount();
886 for ( size_t n = 0; n < nCount; n++ )
887 {
888 if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
889 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") )
890 {
891 index = n;
892 break;
893 }
894 }
895
896 if ( index != wxNOT_FOUND )
897 {
898 // don't throw away fileType that was already found
899 if (!fileType)
900 fileType = new wxFileType;
901 fileType->m_impl->Init(this, index);
902 }
903
904 return fileType;
905 }
906
907 wxString wxMimeTypesManagerImpl::GetCommand(const wxString & verb, size_t nIndex) const
908 {
909 wxString command, testcmd, sV, sTmp;
910 sV = verb + wxT("=");
911
912 // list of verb = command pairs for this mimetype
913 wxMimeTypeCommands * sPairs = m_aEntries [nIndex];
914
915 size_t i;
916 size_t nCount = sPairs->GetCount();
917 for ( i = 0; i < nCount; i++ )
918 {
919 sTmp = sPairs->GetVerbCmd (i);
920 if ( sTmp.Contains(sV) )
921 command = sTmp.AfterFirst(wxT('='));
922 }
923
924 return command;
925 }
926
927 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
928 {
929 InitIfNeeded();
930
931 wxString extensions;
932 const wxArrayString& exts = filetype.GetExtensions();
933 size_t nExts = exts.GetCount();
934 for ( size_t nExt = 0; nExt < nExts; nExt++ )
935 {
936 if ( nExt > 0 )
937 extensions += wxT(' ');
938
939 extensions += exts[nExt];
940 }
941
942 AddMimeTypeInfo(filetype.GetMimeType(),
943 extensions,
944 filetype.GetDescription());
945 }
946
947 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
948 const wxString& strExtensions,
949 const wxString& strDesc)
950 {
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;
956
957 wxArrayString sExts;
958 sTmp.Trim().Trim(false);
959
960 while (!sTmp.empty())
961 {
962 sExts.Add(sTmp.AfterLast(wxT(' ')));
963 sTmp = sTmp.BeforeLast(wxT(' '));
964 }
965
966 AddToMimeData(strMimeType, strIcon, NULL, sExts, strDesc, true);
967 }
968
969 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
970 {
971 InitIfNeeded();
972
973 mimetypes.Empty();
974
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 '*')
979 const wxString &type = m_aTypes[n];
980 if ( type.Find(wxT('*')) == wxNOT_FOUND )
981 {
982 mimetypes.Add(type);
983 }
984 }
985
986 return mimetypes.GetCount();
987 }
988
989 // ----------------------------------------------------------------------------
990 // writing to MIME type files
991 // ----------------------------------------------------------------------------
992
993 bool wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
994 {
995 InitIfNeeded();
996
997 wxArrayString sMimeTypes;
998 ft->GetMimeTypes(sMimeTypes);
999
1000 size_t i;
1001 size_t nCount = sMimeTypes.GetCount();
1002 for (i = 0; i < nCount; i ++)
1003 {
1004 const wxString &sMime = sMimeTypes.Item(i);
1005 int nIndex = m_aTypes.Index(sMime);
1006 if ( nIndex == wxNOT_FOUND)
1007 {
1008 // error if we get here ??
1009 return false;
1010 }
1011 else
1012 {
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);
1018 }
1019 }
1020 // check data integrity
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() );
1025
1026 return true;
1027 }
1028
1029 #endif
1030 // wxUSE_MIMETYPE && wxUSE_FILE