Added the "guessed" icon path for Debian/Corel Linux with KDE
[wxWidgets.git] / src / unix / mimetype.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 license (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "mimetype.h"
14 #endif
15
16 // for compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/defs.h"
25 #endif
26
27 #if (wxUSE_FILE && wxUSE_TEXTFILE) || defined(__WXMSW__)
28
29 #ifndef WX_PRECOMP
30 #include "wx/string.h"
31 #if wxUSE_GUI
32 #include "wx/icon.h"
33 #endif
34 #endif //WX_PRECOMP
35
36
37 #include "wx/log.h"
38 #include "wx/file.h"
39 #include "wx/intl.h"
40 #include "wx/dynarray.h"
41 #include "wx/confbase.h"
42
43 #include "wx/ffile.h"
44 #include "wx/textfile.h"
45 #include "wx/dir.h"
46 #include "wx/utils.h"
47 #include "wx/tokenzr.h"
48
49 #include "wx/unix/mimetype.h"
50
51 // other standard headers
52 #include <ctype.h>
53
54 // in case we're compiling in non-GUI mode
55 class WXDLLEXPORT wxIcon;
56
57 // ----------------------------------------------------------------------------
58 // private classes
59 // ----------------------------------------------------------------------------
60
61
62 // this class uses both mailcap and mime.types to gather information about file
63 // types.
64 //
65 // The information about mailcap file was extracted from metamail(1) sources and
66 // documentation.
67 //
68 // Format of mailcap file: spaces are ignored, each line is either a comment
69 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
70 // A backslash can be used to quote semicolons and newlines (and, in fact,
71 // anything else including itself).
72 //
73 // The first field is always the MIME type in the form of type/subtype (see RFC
74 // 822) where subtype may be '*' meaning "any". Following metamail, we accept
75 // "type" which means the same as "type/*", although I'm not sure whether this
76 // is standard.
77 //
78 // The second field is always the command to run. It is subject to
79 // parameter/filename expansion described below.
80 //
81 // All the following fields are optional and may not be present at all. If
82 // they're present they may appear in any order, although each of them should
83 // appear only once. The optional fields are the following:
84 // * notes=xxx is an uninterpreted string which is silently ignored
85 // * test=xxx is the command to be used to determine whether this mailcap line
86 // applies to our data or not. The RHS of this field goes through the
87 // parameter/filename expansion (as the 2nd field) and the resulting string
88 // is executed. The line applies only if the command succeeds, i.e. returns 0
89 // exit code.
90 // * print=xxx is the command to be used to print (and not view) the data of
91 // this type (parameter/filename expansion is done here too)
92 // * edit=xxx is the command to open/edit the data of this type
93 // * needsterminal means that a new console must be created for the viewer
94 // * copiousoutput means that the viewer doesn't interact with the user but
95 // produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
96 // good example), thus it might be a good idea to use some kind of paging
97 // mechanism.
98 // * textualnewlines means not to perform CR/LF translation (not honored)
99 // * compose and composetyped fields are used to determine the program to be
100 // called to create a new message pert in the specified format (unused).
101 //
102 // Parameter/filename xpansion:
103 // * %s is replaced with the (full) file name
104 // * %t is replaced with MIME type/subtype of the entry
105 // * for multipart type only %n is replaced with the nnumber of parts and %F is
106 // replaced by an array of (content-type, temporary file name) pairs for all
107 // message parts (TODO)
108 // * %{parameter} is replaced with the value of parameter taken from
109 // Content-type header line of the message.
110 //
111 // FIXME any docs with real descriptions of these files??
112 //
113 // There are 2 possible formats for mime.types file, one entry per line (used
114 // for global mime.types) and "expanded" format where an entry takes multiple
115 // lines (used for users mime.types).
116 //
117 // For both formats spaces are ignored and lines starting with a '#' are
118 // comments. Each record has one of two following forms:
119 // a) for "brief" format:
120 // <mime type> <space separated list of extensions>
121 // b) for "expanded" format:
122 // type=<mime type> \ desc="<description>" \ exts="ext"
123 //
124 // We try to autodetect the format of mime.types: if a non-comment line starts
125 // with "type=" we assume the second format, otherwise the first one.
126
127 // there may be more than one entry for one and the same mime type, to
128 // choose the right one we have to run the command specified in the test
129 // field on our data.
130 class MailCapEntry
131 {
132 public:
133 // ctor
134 MailCapEntry(const wxString& openCmd,
135 const wxString& printCmd,
136 const wxString& testCmd)
137 : m_openCmd(openCmd), m_printCmd(printCmd), m_testCmd(testCmd)
138 {
139 m_next = NULL;
140 }
141
142 // accessors
143 const wxString& GetOpenCmd() const { return m_openCmd; }
144 const wxString& GetPrintCmd() const { return m_printCmd; }
145 const wxString& GetTestCmd() const { return m_testCmd; }
146
147 MailCapEntry *GetNext() const { return m_next; }
148
149 // operations
150 // prepend this element to the list
151 void Prepend(MailCapEntry *next) { m_next = next; }
152 // insert into the list at given position
153 void Insert(MailCapEntry *next, size_t pos)
154 {
155 // FIXME slooow...
156 MailCapEntry *cur;
157 size_t n = 0;
158 for ( cur = next; cur != NULL; cur = cur->m_next, n++ ) {
159 if ( n == pos )
160 break;
161 }
162
163 wxASSERT_MSG( n == pos, wxT("invalid position in MailCapEntry::Insert") );
164
165 m_next = cur->m_next;
166 cur->m_next = this;
167 }
168 // append this element to the list
169 void Append(MailCapEntry *next)
170 {
171 wxCHECK_RET( next != NULL, wxT("Append()ing to what?") );
172
173 // FIXME slooow...
174 MailCapEntry *cur;
175 for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
176 ;
177
178 cur->m_next = this;
179
180 wxASSERT_MSG( !m_next, wxT("Append()ing element already in the list?") );
181 }
182
183 private:
184 wxString m_openCmd, // command to use to open/view the file
185 m_printCmd, // print
186 m_testCmd; // only apply this entry if test yields
187 // true (i.e. the command returns 0)
188
189 MailCapEntry *m_next; // in the linked list
190 };
191
192
193 // the base class which may be used to find an icon for the MIME type
194 class wxMimeTypeIconHandler
195 {
196 public:
197 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon) = 0;
198
199 // this function fills manager with MIME types information gathered
200 // (as side effect) when searching for icons. This may be particularly
201 // useful if mime.types is incomplete (e.g. RedHat distributions).
202 virtual void GetMimeInfoRecords(wxMimeTypesManagerImpl *manager) = 0;
203 };
204
205
206 // the icon handler which uses GNOME MIME database
207 class wxGNOMEIconHandler : public wxMimeTypeIconHandler
208 {
209 public:
210 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon);
211 virtual void GetMimeInfoRecords(wxMimeTypesManagerImpl *manager);
212
213 private:
214 void Init();
215 void LoadIconsFromKeyFile(const wxString& filename);
216 void LoadKeyFilesFromDir(const wxString& dirbase);
217
218 void LoadMimeTypesFromMimeFile(const wxString& filename, wxMimeTypesManagerImpl *manager);
219 void LoadMimeFilesFromDir(const wxString& dirbase, wxMimeTypesManagerImpl *manager);
220
221 static bool m_inited;
222
223 static wxSortedArrayString ms_mimetypes;
224 static wxArrayString ms_icons;
225 };
226
227 // the icon handler which uses KDE MIME database
228 class wxKDEIconHandler : public wxMimeTypeIconHandler
229 {
230 public:
231 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon);
232 virtual void GetMimeInfoRecords(wxMimeTypesManagerImpl *manager);
233
234 private:
235 void LoadLinksForMimeSubtype(const wxString& dirbase,
236 const wxString& subdir,
237 const wxString& filename,
238 const wxArrayString& icondirs);
239 void LoadLinksForMimeType(const wxString& dirbase,
240 const wxString& subdir,
241 const wxArrayString& icondirs);
242 void LoadLinkFilesFromDir(const wxString& dirbase,
243 const wxArrayString& icondirs);
244 void Init();
245
246 static bool m_inited;
247
248 static wxSortedArrayString ms_mimetypes;
249 static wxArrayString ms_icons;
250
251 static wxArrayString ms_infoTypes;
252 static wxArrayString ms_infoDescriptions;
253 static wxArrayString ms_infoExtensions;
254 };
255
256
257
258 // ----------------------------------------------------------------------------
259 // various statics
260 // ----------------------------------------------------------------------------
261
262 static wxGNOMEIconHandler gs_iconHandlerGNOME;
263 static wxKDEIconHandler gs_iconHandlerKDE;
264
265 bool wxGNOMEIconHandler::m_inited = FALSE;
266 wxSortedArrayString wxGNOMEIconHandler::ms_mimetypes;
267 wxArrayString wxGNOMEIconHandler::ms_icons;
268
269 bool wxKDEIconHandler::m_inited = FALSE;
270 wxSortedArrayString wxKDEIconHandler::ms_mimetypes;
271 wxArrayString wxKDEIconHandler::ms_icons;
272
273 wxArrayString wxKDEIconHandler::ms_infoTypes;
274 wxArrayString wxKDEIconHandler::ms_infoDescriptions;
275 wxArrayString wxKDEIconHandler::ms_infoExtensions;
276
277
278 ArrayIconHandlers wxMimeTypesManagerImpl::ms_iconHandlers;
279
280 // ----------------------------------------------------------------------------
281 // wxGNOMEIconHandler
282 // ----------------------------------------------------------------------------
283
284 // GNOME stores the info we're interested in in several locations:
285 // 1. xxx.keys files under /usr/share/mime-info
286 // 2. xxx.keys files under ~/.gnome/mime-info
287 //
288 // The format of xxx.keys file is the following:
289 //
290 // mimetype/subtype:
291 // field=value
292 //
293 // with blank lines separating the entries and indented lines starting with
294 // TABs. We're interested in the field icon-filename whose value is the path
295 // containing the icon.
296
297 void wxGNOMEIconHandler::LoadIconsFromKeyFile(const wxString& filename)
298 {
299 wxTextFile textfile(filename);
300 if ( !textfile.Open() )
301 return;
302
303 // values for the entry being parsed
304 wxString curMimeType, curIconFile;
305
306 const wxChar *pc;
307 size_t nLineCount = textfile.GetLineCount();
308 for ( size_t nLine = 0; ; nLine++ )
309 {
310 if ( nLine < nLineCount )
311 {
312 pc = textfile[nLine].c_str();
313 if ( *pc == _T('#') )
314 {
315 // skip comments
316 continue;
317 }
318 }
319 else
320 {
321 // so that we will fall into the "if" below
322 pc = NULL;
323 }
324
325 if ( !pc || !*pc )
326 {
327 // end of the entry
328 if ( !!curMimeType && !!curIconFile )
329 {
330 // do we already know this mimetype?
331 int i = ms_mimetypes.Index(curMimeType);
332 if ( i == wxNOT_FOUND )
333 {
334 // add a new entry
335 size_t n = ms_mimetypes.Add(curMimeType);
336 ms_icons.Insert(curIconFile, n);
337 }
338 else
339 {
340 // replace the existing one (this means that the directories
341 // should be searched in order of increased priority!)
342 ms_icons[(size_t)i] = curIconFile;
343 }
344 }
345
346 if ( !pc )
347 {
348 // the end - this can only happen if nLine == nLineCount
349 break;
350 }
351
352 curIconFile.Empty();
353
354 continue;
355 }
356
357 // what do we have here?
358 if ( *pc == _T('\t') )
359 {
360 // this is a field=value ling
361 pc++; // skip leading TAB
362
363 static const int lenField = 13; // strlen("icon-filename")
364 if ( wxStrncmp(pc, _T("icon-filename"), lenField) == 0 )
365 {
366 // skip '=' which follows and take everything left until the end
367 // of line
368 curIconFile = pc + lenField + 1;
369 }
370 //else: some other field, we don't care
371 }
372 else
373 {
374 // this is the start of the new section
375 curMimeType.Empty();
376
377 while ( *pc != _T(':') && *pc != _T('\0') )
378 {
379 curMimeType += *pc++;
380 }
381 }
382 }
383 }
384
385 void wxGNOMEIconHandler::LoadKeyFilesFromDir(const wxString& dirbase)
386 {
387 wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
388 _T("base directory shouldn't end with a slash") );
389
390 wxString dirname = dirbase;
391 dirname << _T("/mime-info");
392
393 if ( !wxDir::Exists(dirname) )
394 return;
395
396 wxDir dir(dirname);
397 if ( !dir.IsOpened() )
398 return;
399
400 // we will concatenate it with filename to get the full path below
401 dirname += _T('/');
402
403 wxString filename;
404 bool cont = dir.GetFirst(&filename, _T("*.keys"), wxDIR_FILES);
405 while ( cont )
406 {
407 LoadIconsFromKeyFile(dirname + filename);
408
409 cont = dir.GetNext(&filename);
410 }
411 }
412
413
414 void wxGNOMEIconHandler::LoadMimeTypesFromMimeFile(const wxString& filename, wxMimeTypesManagerImpl *manager)
415 {
416 wxTextFile textfile(filename);
417 if ( !textfile.Open() )
418 return;
419
420 // values for the entry being parsed
421 wxString curMimeType, curExtList;
422
423 const wxChar *pc;
424 size_t nLineCount = textfile.GetLineCount();
425 for ( size_t nLine = 0; ; nLine++ )
426 {
427 if ( nLine < nLineCount )
428 {
429 pc = textfile[nLine].c_str();
430 if ( *pc == _T('#') )
431 {
432 // skip comments
433 continue;
434 }
435 }
436 else
437 {
438 // so that we will fall into the "if" below
439 pc = NULL;
440 }
441
442 if ( !pc || !*pc )
443 {
444 // end of the entry
445 if ( !!curMimeType && !!curExtList )
446 {
447 manager -> AddMimeTypeInfo(curMimeType, curExtList, wxEmptyString);
448 }
449
450 if ( !pc )
451 {
452 // the end - this can only happen if nLine == nLineCount
453 break;
454 }
455
456 curExtList.Empty();
457
458 continue;
459 }
460
461 // what do we have here?
462 if ( *pc == _T('\t') )
463 {
464 // this is a field=value ling
465 pc++; // skip leading TAB
466
467 static const int lenField = 4; // strlen("ext:")
468 if ( wxStrncmp(pc, _T("ext:"), lenField) == 0 )
469 {
470 // skip ' ' which follows and take everything left until the end
471 // of line
472 curExtList = pc + lenField + 1;
473 }
474 //else: some other field, we don't care
475 }
476 else
477 {
478 // this is the start of the new section
479 curMimeType.Empty();
480
481 while ( *pc != _T(':') && *pc != _T('\0') )
482 {
483 curMimeType += *pc++;
484 }
485 }
486 }
487 }
488
489
490 void wxGNOMEIconHandler::LoadMimeFilesFromDir(const wxString& dirbase, wxMimeTypesManagerImpl *manager)
491 {
492 wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
493 _T("base directory shouldn't end with a slash") );
494
495 wxString dirname = dirbase;
496 dirname << _T("/mime-info");
497
498 if ( !wxDir::Exists(dirname) )
499 return;
500
501 wxDir dir(dirname);
502 if ( !dir.IsOpened() )
503 return;
504
505 // we will concatenate it with filename to get the full path below
506 dirname += _T('/');
507
508 wxString filename;
509 bool cont = dir.GetFirst(&filename, _T("*.mime"), wxDIR_FILES);
510 while ( cont )
511 {
512 LoadMimeTypesFromMimeFile(dirname + filename, manager);
513
514 cont = dir.GetNext(&filename);
515 }
516 }
517
518
519 void wxGNOMEIconHandler::Init()
520 {
521 wxArrayString dirs;
522 dirs.Add(_T("/usr/share"));
523 dirs.Add(_T("/usr/local/share"));
524
525 wxString gnomedir;
526 wxGetHomeDir( &gnomedir );
527 gnomedir += _T("/.gnome");
528 dirs.Add( gnomedir );
529
530 size_t nDirs = dirs.GetCount();
531 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
532 {
533 LoadKeyFilesFromDir(dirs[nDir]);
534 }
535
536 m_inited = TRUE;
537 }
538
539
540 void wxGNOMEIconHandler::GetMimeInfoRecords(wxMimeTypesManagerImpl *manager)
541 {
542 if ( !m_inited )
543 {
544 Init();
545 }
546
547 wxArrayString dirs;
548 dirs.Add(_T("/usr/share"));
549 dirs.Add(_T("/usr/local/share"));
550
551 wxString gnomedir;
552 wxGetHomeDir( &gnomedir );
553 gnomedir += _T("/.gnome");
554 dirs.Add( gnomedir );
555
556 size_t nDirs = dirs.GetCount();
557 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
558 {
559 LoadMimeFilesFromDir(dirs[nDir], manager);
560 }
561 }
562
563 #if wxUSE_GUI
564 #define WXUNUSED_UNLESS_GUI(p) p
565 #else
566 #define WXUNUSED_UNLESS_GUI(p)
567 #endif
568
569 bool wxGNOMEIconHandler::GetIcon(const wxString& mimetype,
570 wxIcon * WXUNUSED_UNLESS_GUI(icon))
571 {
572 if ( !m_inited )
573 {
574 Init();
575 }
576
577 int index = ms_mimetypes.Index(mimetype);
578 if ( index == wxNOT_FOUND )
579 return FALSE;
580
581 wxString iconname = ms_icons[(size_t)index];
582
583 #if wxUSE_GUI
584 wxLogNull nolog;
585 wxIcon icn;
586 if (iconname.Right(4).MakeUpper() == _T(".XPM"))
587 icn = wxIcon(iconname);
588 else
589 icn = wxIcon(iconname, wxBITMAP_TYPE_ANY);
590 if ( !icn.Ok() )
591 return FALSE;
592
593 if ( icon )
594 *icon = icn;
595 #else
596 // helpful for testing in console mode
597 wxLogDebug(_T("Found GNOME icon for '%s': '%s'\n"),
598 mimetype.c_str(), iconname.c_str());
599 #endif
600
601 return TRUE;
602 }
603
604 // ----------------------------------------------------------------------------
605 // wxKDEIconHandler
606 // ----------------------------------------------------------------------------
607
608 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype
609 // may be found in either of the following locations
610 //
611 // 1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
612 // 2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
613 //
614 // The format of a .kdelnk file is almost the same as the one used by
615 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the
616 // value for the entry "Type"
617
618 void wxKDEIconHandler::LoadLinksForMimeSubtype(const wxString& dirbase,
619 const wxString& subdir,
620 const wxString& filename,
621 const wxArrayString& icondirs)
622 {
623 wxFFile file(dirbase + filename);
624 if ( !file.IsOpened() )
625 return;
626
627 // construct mimetype from the directory name and the basename of the
628 // file (it always has .kdelnk extension)
629 wxString mimetype;
630 mimetype << subdir << _T('/') << filename.BeforeLast(_T('.'));
631
632 // these files are small, slurp the entire file at once
633 wxString text;
634 if ( !file.ReadAll(&text) )
635 return;
636
637 int pos;
638 const wxChar *pc;
639
640 // before trying to find an icon, grab mimetype information
641 // (because BFU's machine would hardly have well-edited mime.types but (s)he might
642 // have edited it in control panel...)
643
644 wxString mime_extension, mime_desc;
645
646 pos = wxNOT_FOUND;
647 if (wxGetLocale() != NULL)
648 mime_desc = _T("Comment[") + wxGetLocale()->GetName() + _T("]=");
649 if (pos == wxNOT_FOUND) mime_desc = _T("Comment=");
650 pos = text.Find(mime_desc);
651 if (pos == wxNOT_FOUND) mime_desc = wxEmptyString;
652 else
653 {
654 pc = text.c_str() + pos + mime_desc.Length();
655 mime_desc = wxEmptyString;
656 while ( *pc && *pc != _T('\n') ) mime_desc += *pc++;
657 }
658
659 pos = text.Find(_T("Patterns="));
660 if (pos != wxNOT_FOUND)
661 {
662 wxString exts;
663 pc = text.c_str() + pos + 9;
664 while ( *pc && *pc != _T('\n') ) exts += *pc++;
665 wxStringTokenizer tokenizer(exts, _T(";"));
666 wxString e;
667
668 while (tokenizer.HasMoreTokens())
669 {
670 e = tokenizer.GetNextToken();
671 if (e.Left(2) != _T("*.")) continue; // don't support too difficult patterns
672 mime_extension << e.Mid(2);
673 mime_extension << _T(' ');
674 }
675 mime_extension.RemoveLast();
676 }
677
678 ms_infoTypes.Add(mimetype);
679 ms_infoDescriptions.Add(mime_desc);
680 ms_infoExtensions.Add(mime_extension);
681
682 // ok, now we can take care of icon:
683
684 pos = text.Find(_T("Icon="));
685 if ( pos == wxNOT_FOUND )
686 {
687 // no icon info
688 return;
689 }
690
691 wxString icon;
692
693 pc = text.c_str() + pos + 5; // 5 == strlen("Icon=")
694 while ( *pc && *pc != _T('\n') )
695 {
696 icon += *pc++;
697 }
698
699 if ( !!icon )
700 {
701 // we must check if the file exists because it may be stored
702 // in many locations, at least ~/.kde and $KDEDIR
703 size_t nDir, nDirs = icondirs.GetCount();
704 for ( nDir = 0; nDir < nDirs; nDir++ )
705 if (wxFileExists(icondirs[nDir] + icon))
706 {
707 icon.Prepend(icondirs[nDir]);
708 break;
709 }
710 if (nDir == nDirs) return; //does not exist
711
712 // do we already have this MIME type?
713 int i = ms_mimetypes.Index(mimetype);
714 if ( i == wxNOT_FOUND )
715 {
716 // add it
717 size_t n = ms_mimetypes.Add(mimetype);
718 ms_icons.Insert(icon, n);
719 }
720 else
721 {
722 // replace the old value
723 ms_icons[(size_t)i] = icon;
724 }
725 }
726 }
727
728 void wxKDEIconHandler::LoadLinksForMimeType(const wxString& dirbase,
729 const wxString& subdir,
730 const wxArrayString& icondirs)
731 {
732 wxString dirname = dirbase;
733 dirname += subdir;
734 wxDir dir(dirname);
735 if ( !dir.IsOpened() )
736 return;
737
738 dirname += _T('/');
739
740 wxString filename;
741 bool cont = dir.GetFirst(&filename, _T("*.kdelnk"), wxDIR_FILES);
742 while ( cont )
743 {
744 LoadLinksForMimeSubtype(dirname, subdir, filename, icondirs);
745
746 cont = dir.GetNext(&filename);
747 }
748 }
749
750 void wxKDEIconHandler::LoadLinkFilesFromDir(const wxString& dirbase,
751 const wxArrayString& icondirs)
752 {
753 wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
754 _T("base directory shouldn't end with a slash") );
755
756 wxString dirname = dirbase;
757 dirname << _T("/mimelnk");
758
759 if ( !wxDir::Exists(dirname) )
760 return;
761
762 wxDir dir(dirname);
763 if ( !dir.IsOpened() )
764 return;
765
766 // we will concatenate it with dir name to get the full path below
767 dirname += _T('/');
768
769 wxString subdir;
770 bool cont = dir.GetFirst(&subdir, wxEmptyString, wxDIR_DIRS);
771 while ( cont )
772 {
773 LoadLinksForMimeType(dirname, subdir, icondirs);
774
775 cont = dir.GetNext(&subdir);
776 }
777 }
778
779 void wxKDEIconHandler::Init()
780 {
781 wxArrayString dirs;
782 wxArrayString icondirs;
783
784 // settings in ~/.kde have maximal priority
785 dirs.Add(wxGetHomeDir() + _T("/.kde/share"));
786 icondirs.Add(wxGetHomeDir() + _T("/.kde/share/icons/"));
787
788 // the variable KDEDIR is set when KDE is running
789 const char *kdedir = getenv("KDEDIR");
790 if ( kdedir )
791 {
792 dirs.Add(wxString(kdedir) + _T("/share"));
793 icondirs.Add(wxString(kdedir) + _T("/share/icons/"));
794 }
795 else
796 {
797 // try to guess KDEDIR
798 dirs.Add(_T("/usr/share"));
799 dirs.Add(_T("/opt/kde/share"));
800 icondirs.Add(_T("/usr/share/icons/"));
801 icondirs.Add(_T("/usr/X11R6/share/icons/")); // Debian/Corel linux
802 icondirs.Add(_T("/opt/kde/share/icons/"));
803 }
804
805 size_t nDirs = dirs.GetCount();
806 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
807 {
808 LoadLinkFilesFromDir(dirs[nDir], icondirs);
809 }
810
811 m_inited = TRUE;
812 }
813
814 bool wxKDEIconHandler::GetIcon(const wxString& mimetype,
815 wxIcon * WXUNUSED_UNLESS_GUI(icon))
816 {
817 if ( !m_inited )
818 {
819 Init();
820 }
821
822 int index = ms_mimetypes.Index(mimetype);
823 if ( index == wxNOT_FOUND )
824 return FALSE;
825
826 wxString iconname = ms_icons[(size_t)index];
827
828 #if wxUSE_GUI
829 wxLogNull nolog;
830 wxIcon icn;
831 if (iconname.Right(4).MakeUpper() == _T(".XPM"))
832 icn = wxIcon(iconname);
833 else
834 icn = wxIcon(iconname, wxBITMAP_TYPE_ANY);
835
836 if ( !icn.Ok() )
837 return FALSE;
838
839 if ( icon )
840 *icon = icn;
841 #else
842 // helpful for testing in console mode
843 wxLogDebug(_T("Found KDE icon for '%s': '%s'\n"),
844 mimetype.c_str(), iconname.c_str());
845 #endif
846
847 return TRUE;
848 }
849
850
851 void wxKDEIconHandler::GetMimeInfoRecords(wxMimeTypesManagerImpl *manager)
852 {
853 if ( !m_inited ) Init();
854
855 size_t cnt = ms_infoTypes.GetCount();
856 for (unsigned i = 0; i < cnt; i++)
857 manager -> AddMimeTypeInfo(ms_infoTypes[i], ms_infoExtensions[i], ms_infoDescriptions[i]);
858 }
859
860
861 // ----------------------------------------------------------------------------
862 // wxFileTypeImpl (Unix)
863 // ----------------------------------------------------------------------------
864
865 MailCapEntry *
866 wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters& params) const
867 {
868 wxString command;
869 MailCapEntry *entry = m_manager->m_aEntries[m_index[0]];
870 while ( entry != NULL ) {
871 // notice that an empty command would always succeed (it's ok)
872 command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
873
874 if ( command.IsEmpty() || (wxSystem(command) == 0) ) {
875 // ok, passed
876 wxLogTrace(wxT("Test '%s' for mime type '%s' succeeded."),
877 command.c_str(), params.GetMimeType().c_str());
878 break;
879 }
880 else {
881 wxLogTrace(wxT("Test '%s' for mime type '%s' failed."),
882 command.c_str(), params.GetMimeType().c_str());
883 }
884
885 entry = entry->GetNext();
886 }
887
888 return entry;
889 }
890
891 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
892 {
893 wxArrayString mimetypes;
894 GetMimeTypes(mimetypes);
895
896 ArrayIconHandlers& handlers = m_manager->GetIconHandlers();
897 size_t count = handlers.GetCount();
898 size_t counttypes = mimetypes.GetCount();
899 for ( size_t n = 0; n < count; n++ )
900 {
901 for ( size_t n2 = 0; n2 < counttypes; n2++ )
902 {
903 if ( handlers[n]->GetIcon(mimetypes[n2], icon) )
904 return TRUE;
905 }
906 }
907
908 return FALSE;
909 }
910
911
912 bool
913 wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
914 {
915 mimeTypes.Clear();
916 for (size_t i = 0; i < m_index.GetCount(); i++)
917 mimeTypes.Add(m_manager->m_aTypes[m_index[i]]);
918 return TRUE;
919 }
920
921
922 bool
923 wxFileTypeImpl::GetExpandedCommand(wxString *expandedCmd,
924 const wxFileType::MessageParameters& params,
925 bool open) const
926 {
927 MailCapEntry *entry = GetEntry(params);
928 if ( entry == NULL ) {
929 // all tests failed...
930 return FALSE;
931 }
932
933 wxString cmd = open ? entry->GetOpenCmd() : entry->GetPrintCmd();
934 if ( cmd.IsEmpty() ) {
935 // may happen, especially for "print"
936 return FALSE;
937 }
938
939 *expandedCmd = wxFileType::ExpandCommand(cmd, params);
940 return TRUE;
941 }
942
943 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
944 {
945 wxString strExtensions = m_manager->GetExtension(m_index[0]);
946 extensions.Empty();
947
948 // one extension in the space or comma delimitid list
949 wxString strExt;
950 for ( const wxChar *p = strExtensions; ; p++ ) {
951 if ( *p == wxT(' ') || *p == wxT(',') || *p == wxT('\0') ) {
952 if ( !strExt.IsEmpty() ) {
953 extensions.Add(strExt);
954 strExt.Empty();
955 }
956 //else: repeated spaces (shouldn't happen, but it's not that
957 // important if it does happen)
958
959 if ( *p == wxT('\0') )
960 break;
961 }
962 else if ( *p == wxT('.') ) {
963 // remove the dot from extension (but only if it's the first char)
964 if ( !strExt.IsEmpty() ) {
965 strExt += wxT('.');
966 }
967 //else: no, don't append it
968 }
969 else {
970 strExt += *p;
971 }
972 }
973
974 return TRUE;
975 }
976
977 // ----------------------------------------------------------------------------
978 // wxMimeTypesManagerImpl (Unix)
979 // ----------------------------------------------------------------------------
980
981 /* static */
982 ArrayIconHandlers& wxMimeTypesManagerImpl::GetIconHandlers()
983 {
984 if ( ms_iconHandlers.GetCount() == 0 )
985 {
986 ms_iconHandlers.Add(&gs_iconHandlerGNOME);
987 ms_iconHandlers.Add(&gs_iconHandlerKDE);
988 }
989
990 return ms_iconHandlers;
991 }
992
993 // read system and user mailcaps (TODO implement mime.types support)
994 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
995 {
996 // directories where we look for mailcap and mime.types by default
997 // (taken from metamail(1) sources)
998 static const wxChar *aStandardLocations[] =
999 {
1000 wxT("/etc"),
1001 wxT("/usr/etc"),
1002 wxT("/usr/local/etc"),
1003 wxT("/etc/mail"),
1004 wxT("/usr/public/lib")
1005 };
1006
1007 // first read the system wide file(s)
1008 size_t n;
1009 for ( n = 0; n < WXSIZEOF(aStandardLocations); n++ ) {
1010 wxString dir = aStandardLocations[n];
1011
1012 wxString file = dir + wxT("/mailcap");
1013 if ( wxFile::Exists(file) ) {
1014 ReadMailcap(file);
1015 }
1016
1017 file = dir + wxT("/mime.types");
1018 if ( wxFile::Exists(file) ) {
1019 ReadMimeTypes(file);
1020 }
1021 }
1022
1023 wxString strHome = wxGetenv(wxT("HOME"));
1024
1025 // and now the users mailcap
1026 wxString strUserMailcap = strHome + wxT("/.mailcap");
1027 if ( wxFile::Exists(strUserMailcap) ) {
1028 ReadMailcap(strUserMailcap);
1029 }
1030
1031 // read the users mime.types
1032 wxString strUserMimeTypes = strHome + wxT("/.mime.types");
1033 if ( wxFile::Exists(strUserMimeTypes) ) {
1034 ReadMimeTypes(strUserMimeTypes);
1035 }
1036
1037 // read KDE/GNOME tables
1038 ArrayIconHandlers& handlers = GetIconHandlers();
1039 size_t count = handlers.GetCount();
1040 for ( size_t hn = 0; hn < count; hn++ )
1041 handlers[hn]->GetMimeInfoRecords(this);
1042 }
1043
1044 wxFileType *
1045 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
1046 {
1047 wxFileType *fileType = NULL;
1048 size_t count = m_aExtensions.GetCount();
1049 for ( size_t n = 0; n < count; n++ ) {
1050 wxString extensions = m_aExtensions[n];
1051 while ( !extensions.IsEmpty() ) {
1052 wxString field = extensions.BeforeFirst(wxT(' '));
1053 extensions = extensions.AfterFirst(wxT(' '));
1054
1055 // consider extensions as not being case-sensitive
1056 if ( field.IsSameAs(ext, FALSE /* no case */) ) {
1057 // found
1058 if (fileType == NULL) fileType = new wxFileType;
1059 fileType->m_impl->Init(this, n);
1060 // adds this mime type to _list_ of mime types with this extension
1061 }
1062 }
1063 }
1064
1065 return fileType;
1066 }
1067
1068 wxFileType *
1069 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
1070 {
1071 // mime types are not case-sensitive
1072 wxString mimetype(mimeType);
1073 mimetype.MakeLower();
1074
1075 // first look for an exact match
1076 int index = m_aTypes.Index(mimetype);
1077 if ( index == wxNOT_FOUND ) {
1078 // then try to find "text/*" as match for "text/plain" (for example)
1079 // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
1080 // the whole string - ok.
1081 wxString strCategory = mimetype.BeforeFirst(wxT('/'));
1082
1083 size_t nCount = m_aTypes.Count();
1084 for ( size_t n = 0; n < nCount; n++ ) {
1085 if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
1086 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") ) {
1087 index = n;
1088 break;
1089 }
1090 }
1091 }
1092
1093 if ( index != wxNOT_FOUND ) {
1094 wxFileType *fileType = new wxFileType;
1095 fileType->m_impl->Init(this, index);
1096
1097 return fileType;
1098 }
1099 else {
1100 // not found...
1101 return NULL;
1102 }
1103 }
1104
1105 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
1106 {
1107 wxString extensions;
1108 const wxArrayString& exts = filetype.GetExtensions();
1109 size_t nExts = exts.GetCount();
1110 for ( size_t nExt = 0; nExt < nExts; nExt++ ) {
1111 if ( nExt > 0 ) {
1112 extensions += wxT(' ');
1113 }
1114 extensions += exts[nExt];
1115 }
1116
1117 AddMimeTypeInfo(filetype.GetMimeType(),
1118 extensions,
1119 filetype.GetDescription());
1120
1121 AddMailcapInfo(filetype.GetMimeType(),
1122 filetype.GetOpenCommand(),
1123 filetype.GetPrintCommand(),
1124 wxT(""),
1125 filetype.GetDescription());
1126 }
1127
1128 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
1129 const wxString& strExtensions,
1130 const wxString& strDesc)
1131 {
1132 int index = m_aTypes.Index(strMimeType);
1133 if ( index == wxNOT_FOUND ) {
1134 // add a new entry
1135 m_aTypes.Add(strMimeType);
1136 m_aEntries.Add(NULL);
1137 m_aExtensions.Add(strExtensions);
1138 m_aDescriptions.Add(strDesc);
1139 }
1140 else {
1141 // modify an existing one
1142 if ( !strDesc.IsEmpty() ) {
1143 m_aDescriptions[index] = strDesc; // replace old value
1144 }
1145 m_aExtensions[index] += ' ' + strExtensions;
1146 }
1147 }
1148
1149 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString& strType,
1150 const wxString& strOpenCmd,
1151 const wxString& strPrintCmd,
1152 const wxString& strTest,
1153 const wxString& strDesc)
1154 {
1155 MailCapEntry *entry = new MailCapEntry(strOpenCmd, strPrintCmd, strTest);
1156
1157 int nIndex = m_aTypes.Index(strType);
1158 if ( nIndex == wxNOT_FOUND ) {
1159 // new file type
1160 m_aTypes.Add(strType);
1161
1162 m_aEntries.Add(entry);
1163 m_aExtensions.Add(wxT(""));
1164 m_aDescriptions.Add(strDesc);
1165 }
1166 else {
1167 // always append the entry in the tail of the list - info added with
1168 // this function can only come from AddFallbacks()
1169 MailCapEntry *entryOld = m_aEntries[nIndex];
1170 if ( entryOld )
1171 entry->Append(entryOld);
1172 else
1173 m_aEntries[nIndex] = entry;
1174 }
1175 }
1176
1177 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
1178 {
1179 wxLogTrace(wxT("--- Parsing mime.types file '%s' ---"), strFileName.c_str());
1180
1181 wxTextFile file(strFileName);
1182 if ( !file.Open() )
1183 return FALSE;
1184
1185 // the information we extract
1186 wxString strMimeType, strDesc, strExtensions;
1187
1188 size_t nLineCount = file.GetLineCount();
1189 const wxChar *pc = NULL;
1190 for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
1191 if ( pc == NULL ) {
1192 // now we're at the start of the line
1193 pc = file[nLine].c_str();
1194 }
1195 else {
1196 // we didn't finish with the previous line yet
1197 nLine--;
1198 }
1199
1200 // skip whitespace
1201 while ( wxIsspace(*pc) )
1202 pc++;
1203
1204 // comment or blank line?
1205 if ( *pc == wxT('#') || !*pc ) {
1206 // skip the whole line
1207 pc = NULL;
1208 continue;
1209 }
1210
1211 // detect file format
1212 const wxChar *pEqualSign = wxStrchr(pc, wxT('='));
1213 if ( pEqualSign == NULL ) {
1214 // brief format
1215 // ------------
1216
1217 // first field is mime type
1218 for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != wxT('\0'); pc++ ) {
1219 strMimeType += *pc;
1220 }
1221
1222 // skip whitespace
1223 while ( wxIsspace(*pc) )
1224 pc++;
1225
1226 // take all the rest of the string
1227 strExtensions = pc;
1228
1229 // no description...
1230 strDesc.Empty();
1231 }
1232 else {
1233 // expanded format
1234 // ---------------
1235
1236 // the string on the left of '=' is the field name
1237 wxString strLHS(pc, pEqualSign - pc);
1238
1239 // eat whitespace
1240 for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
1241 ;
1242
1243 const wxChar *pEnd;
1244 if ( *pc == wxT('"') ) {
1245 // the string is quoted and ends at the matching quote
1246 pEnd = wxStrchr(++pc, wxT('"'));
1247 if ( pEnd == NULL ) {
1248 wxLogWarning(_("Mime.types file %s, line %d: unterminated "
1249 "quoted string."),
1250 strFileName.c_str(), nLine + 1);
1251 }
1252 }
1253 else {
1254 // unquoted string ends at the first space
1255 for ( pEnd = pc; !wxIsspace(*pEnd); pEnd++ )
1256 ;
1257 }
1258
1259 // now we have the RHS (field value)
1260 wxString strRHS(pc, pEnd - pc);
1261
1262 // check what follows this entry
1263 if ( *pEnd == wxT('"') ) {
1264 // skip this quote
1265 pEnd++;
1266 }
1267
1268 for ( pc = pEnd; wxIsspace(*pc); pc++ )
1269 ;
1270
1271 // if there is something left, it may be either a '\\' to continue
1272 // the line or the next field of the same entry
1273 bool entryEnded = *pc == wxT('\0'),
1274 nextFieldOnSameLine = FALSE;
1275 if ( !entryEnded ) {
1276 nextFieldOnSameLine = ((*pc != wxT('\\')) || (pc[1] != wxT('\0')));
1277 }
1278
1279 // now see what we got
1280 if ( strLHS == wxT("type") ) {
1281 strMimeType = strRHS;
1282 }
1283 else if ( strLHS == wxT("desc") ) {
1284 strDesc = strRHS;
1285 }
1286 else if ( strLHS == wxT("exts") ) {
1287 strExtensions = strRHS;
1288 }
1289 else {
1290 wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
1291 strFileName.c_str(), nLine + 1, strLHS.c_str());
1292 }
1293
1294 if ( !entryEnded ) {
1295 if ( !nextFieldOnSameLine )
1296 pc = NULL;
1297 //else: don't reset it
1298
1299 // as we don't reset strMimeType, the next field in this entry
1300 // will be interpreted correctly.
1301
1302 continue;
1303 }
1304 }
1305
1306 // although it doesn't seem to be covered by RFCs, some programs
1307 // (notably Netscape) create their entries with several comma
1308 // separated extensions (RFC mention the spaces only)
1309 strExtensions.Replace(wxT(","), wxT(" "));
1310
1311 // also deal with the leading dot
1312 if ( !strExtensions.IsEmpty() && strExtensions[0u] == wxT('.') )
1313 {
1314 strExtensions.erase(0, 1);
1315 }
1316
1317 AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
1318
1319 // finished with this line
1320 pc = NULL;
1321 }
1322
1323 // check our data integriry
1324 wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
1325 m_aTypes.Count() == m_aExtensions.Count() &&
1326 m_aTypes.Count() == m_aDescriptions.Count() );
1327
1328 return TRUE;
1329 }
1330
1331 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
1332 bool fallback)
1333 {
1334 wxLogTrace(wxT("--- Parsing mailcap file '%s' ---"), strFileName.c_str());
1335
1336 wxTextFile file(strFileName);
1337 if ( !file.Open() )
1338 return FALSE;
1339
1340 // see the comments near the end of function for the reason we need these
1341 // variables (search for the next occurence of them)
1342 // indices of MIME types (in m_aTypes) we already found in this file
1343 wxArrayInt aEntryIndices;
1344 // aLastIndices[n] is the index of last element in
1345 // m_aEntries[aEntryIndices[n]] from this file
1346 wxArrayInt aLastIndices;
1347
1348 size_t nLineCount = file.GetLineCount();
1349 for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
1350 // now we're at the start of the line
1351 const wxChar *pc = file[nLine].c_str();
1352
1353 // skip whitespace
1354 while ( wxIsspace(*pc) )
1355 pc++;
1356
1357 // comment or empty string?
1358 if ( *pc == wxT('#') || *pc == wxT('\0') )
1359 continue;
1360
1361 // no, do parse
1362
1363 // what field are we currently in? The first 2 are fixed and there may
1364 // be an arbitrary number of other fields -- currently, we are not
1365 // interested in any of them, but we should parse them as well...
1366 enum
1367 {
1368 Field_Type,
1369 Field_OpenCmd,
1370 Field_Other
1371 } currentToken = Field_Type;
1372
1373 // the flags and field values on the current line
1374 bool needsterminal = FALSE,
1375 copiousoutput = FALSE;
1376 wxString strType,
1377 strOpenCmd,
1378 strPrintCmd,
1379 strTest,
1380 strDesc,
1381 curField; // accumulator
1382 for ( bool cont = TRUE; cont; pc++ ) {
1383 switch ( *pc ) {
1384 case wxT('\\'):
1385 // interpret the next character literally (notice that
1386 // backslash can be used for line continuation)
1387 if ( *++pc == wxT('\0') ) {
1388 // fetch the next line.
1389
1390 // pc currently points to nowhere, but after the next
1391 // pc++ in the for line it will point to the beginning
1392 // of the next line in the file
1393 pc = file[++nLine].c_str() - 1;
1394 }
1395 else {
1396 // just a normal character
1397 curField += *pc;
1398 }
1399 break;
1400
1401 case wxT('\0'):
1402 cont = FALSE; // end of line reached, exit the loop
1403
1404 // fall through
1405
1406 case wxT(';'):
1407 // store this field and start looking for the next one
1408
1409 // trim whitespaces from both sides
1410 curField.Trim(TRUE).Trim(FALSE);
1411
1412 switch ( currentToken ) {
1413 case Field_Type:
1414 strType = curField;
1415 if ( strType.Find(wxT('/')) == wxNOT_FOUND ) {
1416 // we interpret "type" as "type/*"
1417 strType += wxT("/*");
1418 }
1419
1420 currentToken = Field_OpenCmd;
1421 break;
1422
1423 case Field_OpenCmd:
1424 strOpenCmd = curField;
1425
1426 currentToken = Field_Other;
1427 break;
1428
1429 case Field_Other:
1430 {
1431 // "good" mailcap entry?
1432 bool ok = TRUE;
1433
1434 // is this something of the form foo=bar?
1435 const wxChar *pEq = wxStrchr(curField, wxT('='));
1436 if ( pEq != NULL ) {
1437 wxString lhs = curField.BeforeFirst(wxT('=')),
1438 rhs = curField.AfterFirst(wxT('='));
1439
1440 lhs.Trim(TRUE); // from right
1441 rhs.Trim(FALSE); // from left
1442
1443 if ( lhs == wxT("print") )
1444 strPrintCmd = rhs;
1445 else if ( lhs == wxT("test") )
1446 strTest = rhs;
1447 else if ( lhs == wxT("description") ) {
1448 // it might be quoted
1449 if ( rhs[0u] == wxT('"') &&
1450 rhs.Last() == wxT('"') ) {
1451 strDesc = wxString(rhs.c_str() + 1,
1452 rhs.Len() - 2);
1453 }
1454 else {
1455 strDesc = rhs;
1456 }
1457 }
1458 else if ( lhs == wxT("compose") ||
1459 lhs == wxT("composetyped") ||
1460 lhs == wxT("notes") ||
1461 lhs == wxT("edit") )
1462 ; // ignore
1463 else
1464 ok = FALSE;
1465
1466 }
1467 else {
1468 // no, it's a simple flag
1469 // TODO support the flags:
1470 // 1. create an xterm for 'needsterminal'
1471 // 2. append "| $PAGER" for 'copiousoutput'
1472 if ( curField == wxT("needsterminal") )
1473 needsterminal = TRUE;
1474 else if ( curField == wxT("copiousoutput") )
1475 copiousoutput = TRUE;
1476 else if ( curField == wxT("textualnewlines") )
1477 ; // ignore
1478 else
1479 ok = FALSE;
1480 }
1481
1482 if ( !ok )
1483 {
1484 // we don't understand this field, but
1485 // Netscape stores info in it, so don't warn
1486 // about it
1487 if ( curField.Left(16u) != "x-mozilla-flags=" )
1488 {
1489 // don't flood the user with error
1490 // messages if we don't understand
1491 // something in his mailcap, but give
1492 // them in debug mode because this might
1493 // be useful for the programmer
1494 wxLogDebug
1495 (
1496 wxT("Mailcap file %s, line %d: "
1497 "unknown field '%s' for the "
1498 "MIME type '%s' ignored."),
1499 strFileName.c_str(),
1500 nLine + 1,
1501 curField.c_str(),
1502 strType.c_str()
1503 );
1504 }
1505 }
1506 }
1507
1508 // it already has this value
1509 //currentToken = Field_Other;
1510 break;
1511
1512 default:
1513 wxFAIL_MSG(wxT("unknown field type in mailcap"));
1514 }
1515
1516 // next token starts immediately after ';'
1517 curField.Empty();
1518 break;
1519
1520 default:
1521 curField += *pc;
1522 }
1523 }
1524
1525 // check that we really read something reasonable
1526 if ( currentToken == Field_Type || currentToken == Field_OpenCmd ) {
1527 wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
1528 "ignored."),
1529 strFileName.c_str(), nLine + 1);
1530 }
1531 else {
1532 MailCapEntry *entry = new MailCapEntry(strOpenCmd,
1533 strPrintCmd,
1534 strTest);
1535
1536 // NB: because of complications below (we must get entries priority
1537 // right), we can't use AddMailcapInfo() here, unfortunately.
1538 strType.MakeLower();
1539 int nIndex = m_aTypes.Index(strType);
1540 if ( nIndex == wxNOT_FOUND ) {
1541 // new file type
1542 m_aTypes.Add(strType);
1543
1544 m_aEntries.Add(entry);
1545 m_aExtensions.Add(wxT(""));
1546 m_aDescriptions.Add(strDesc);
1547 }
1548 else {
1549 // modify the existing entry: the entries in one and the same
1550 // file are read in top-to-bottom order, i.e. the entries read
1551 // first should be tried before the entries below. However,
1552 // the files read later should override the settings in the
1553 // files read before (except if fallback is TRUE), thus we
1554 // Insert() the new entry to the list if it has already
1555 // occured in _this_ file, but Prepend() it if it occured in
1556 // some of the previous ones and Append() to it in the
1557 // fallback case
1558
1559 if ( fallback ) {
1560 // 'fallback' parameter prevents the entries from this
1561 // file from overriding the other ones - always append
1562 MailCapEntry *entryOld = m_aEntries[nIndex];
1563 if ( entryOld )
1564 entry->Append(entryOld);
1565 else
1566 m_aEntries[nIndex] = entry;
1567 }
1568 else {
1569 int entryIndex = aEntryIndices.Index(nIndex);
1570 if ( entryIndex == wxNOT_FOUND ) {
1571 // first time in this file
1572 aEntryIndices.Add(nIndex);
1573 aLastIndices.Add(0);
1574
1575 entry->Prepend(m_aEntries[nIndex]);
1576 m_aEntries[nIndex] = entry;
1577 }
1578 else {
1579 // not the first time in _this_ file
1580 size_t nEntryIndex = (size_t)entryIndex;
1581 MailCapEntry *entryOld = m_aEntries[nIndex];
1582 if ( entryOld )
1583 entry->Insert(entryOld, aLastIndices[nEntryIndex]);
1584 else
1585 m_aEntries[nIndex] = entry;
1586
1587 // the indices were shifted by 1
1588 aLastIndices[nEntryIndex]++;
1589 }
1590 }
1591
1592 if ( !strDesc.IsEmpty() ) {
1593 // replace the old one - what else can we do??
1594 m_aDescriptions[nIndex] = strDesc;
1595 }
1596 }
1597 }
1598
1599 // check our data integriry
1600 wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
1601 m_aTypes.Count() == m_aExtensions.Count() &&
1602 m_aTypes.Count() == m_aDescriptions.Count() );
1603 }
1604
1605 return TRUE;
1606 }
1607
1608 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
1609 {
1610 mimetypes.Empty();
1611
1612 wxString type;
1613 size_t count = m_aTypes.GetCount();
1614 for ( size_t n = 0; n < count; n++ )
1615 {
1616 // don't return template types from here (i.e. anything containg '*')
1617 type = m_aTypes[n];
1618 if ( type.Find(_T('*')) == wxNOT_FOUND )
1619 {
1620 mimetypes.Add(type);
1621 }
1622 }
1623
1624 return mimetypes.GetCount();
1625 }
1626
1627 #endif
1628 // wxUSE_FILE && wxUSE_TEXTFILE
1629