]> git.saurik.com Git - wxWidgets.git/blame - src/common/mimetype.cpp
Added #if wxUSE_OWNER_DRAWN
[wxWidgets.git] / src / common / mimetype.cpp
CommitLineData
b13d92d1
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: common/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__
ce4169a4 13#pragma implementation "mimetype.h"
b13d92d1
VZ
14#endif
15
b13d92d1
VZ
16// for compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
ce4169a4 20 #pragma hdrstop
b13d92d1
VZ
21#endif
22
b13d92d1 23#ifndef WX_PRECOMP
ce4169a4
RR
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 #include "wx/icon.h"
b13d92d1
VZ
32#endif //WX_PRECOMP
33
3d05544e
JS
34// Doesn't compile in WIN16 mode
35#ifndef __WIN16__
36
b13d92d1 37#include "wx/log.h"
ce4169a4 38#include "wx/file.h"
b13d92d1
VZ
39#include "wx/intl.h"
40#include "wx/dynarray.h"
2432b92d 41#include "wx/confbase.h"
b13d92d1
VZ
42
43#ifdef __WXMSW__
44 #include "wx/msw/registry.h"
2432b92d 45 #include "windows.h"
fb5ddb4c 46#elif defined(__UNIX__) || defined(__WXPM__)
b9517a0a 47 #include "wx/ffile.h"
b13d92d1 48 #include "wx/textfile.h"
b9517a0a
VZ
49 #include "wx/dir.h"
50 #include "wx/utils.h"
b13d92d1
VZ
51#endif // OS
52
53#include "wx/mimetype.h"
54
55// other standard headers
56#include <ctype.h>
57
58// ----------------------------------------------------------------------------
59// private classes
60// ----------------------------------------------------------------------------
61
62// implementation classes, platform dependent
63#ifdef __WXMSW__
64
65// These classes use Windows registry to retrieve the required information.
66//
67// Keys used (not all of them are documented, so it might actually stop working
68// in futur versions of Windows...):
69// 1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME
70// types, each key has a string value "Extension" which gives (dot preceded)
71// extension for the files of this MIME type.
72//
73// 2. "HKCR\.ext" contains
74// a) unnamed value containing the "filetype"
75// b) value "Content Type" containing the MIME type
76//
77// 3. "HKCR\filetype" contains
78// a) unnamed value containing the description
79// b) subkey "DefaultIcon" with single unnamed value giving the icon index in
80// an icon file
81// c) shell\open\command and shell\open\print subkeys containing the commands
82// to open/print the file (the positional parameters are introduced by %1,
83// %2, ... in these strings, we change them to %s ourselves)
84
1b986aef
VZ
85// although I don't know of any official documentation which mentions this
86// location, uses it, so it isn't likely to change
87static const wxChar *MIME_DATABASE_KEY = wxT("MIME\\Database\\Content Type\\");
88
b13d92d1
VZ
89class wxFileTypeImpl
90{
91public:
92 // ctor
8e124873 93 wxFileTypeImpl() { m_info = NULL; }
b13d92d1 94
8e124873
VZ
95 // one of these Init() function must be called (ctor can't take any
96 // arguments because it's common)
97
98 // initialize us with our file type name and extension - in this case
99 // we will read all other data from the registry
100 void Init(const wxString& strFileType, const wxString& ext)
101 { m_strFileType = strFileType; m_ext = ext; }
102
103 // initialize us with a wxFileTypeInfo object - it contains all the
104 // data
105 void Init(const wxFileTypeInfo& info)
106 { m_info = &info; }
b13d92d1
VZ
107
108 // implement accessor functions
109 bool GetExtensions(wxArrayString& extensions);
110 bool GetMimeType(wxString *mimeType) const;
111 bool GetIcon(wxIcon *icon) const;
112 bool GetDescription(wxString *desc) const;
113 bool GetOpenCommand(wxString *openCmd,
8e124873 114 const wxFileType::MessageParameters& params) const;
b13d92d1 115 bool GetPrintCommand(wxString *printCmd,
8e124873 116 const wxFileType::MessageParameters& params) const;
b13d92d1
VZ
117
118private:
8e124873
VZ
119 // helper function: reads the command corresponding to the specified verb
120 // from the registry (returns an empty string if not found)
121 wxString GetCommand(const wxChar *verb) const;
122
123 // we use either m_info or read the data from the registry if m_info == NULL
124 const wxFileTypeInfo *m_info;
c61f4f6d
VZ
125 wxString m_strFileType, // may be empty
126 m_ext;
b13d92d1
VZ
127};
128
a497618a 129WX_DECLARE_EXPORTED_OBJARRAY(wxFileTypeInfo, wxArrayFileTypeInfo);
8e124873
VZ
130#include "wx/arrimpl.cpp"
131WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo);
132
b13d92d1
VZ
133class wxMimeTypesManagerImpl
134{
135public:
136 // nothing to do here, we don't load any data but just go and fetch it from
137 // the registry when asked for
138 wxMimeTypesManagerImpl() { }
139
140 // implement containing class functions
141 wxFileType *GetFileTypeFromExtension(const wxString& ext);
142 wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
143
696e1ea0 144 size_t EnumAllFileTypes(wxArrayString& mimetypes);
1b986aef 145
b13d92d1 146 // this are NOPs under Windows
be1023d5
VZ
147 bool ReadMailcap(const wxString& filename, bool fallback = TRUE)
148 { return TRUE; }
149 bool ReadMimeTypes(const wxString& filename)
150 { return TRUE; }
8e124873
VZ
151
152 void AddFallback(const wxFileTypeInfo& ft) { m_fallbacks.Add(ft); }
153
154private:
155 wxArrayFileTypeInfo m_fallbacks;
b13d92d1
VZ
156};
157
7c74e7fe
SC
158#elif defined( __WXMAC__ )
159
160WX_DECLARE_EXPORTED_OBJARRAY(wxFileTypeInfo, wxArrayFileTypeInfo);
161#include "wx/arrimpl.cpp"
162WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo);
163
164class wxMimeTypesManagerImpl
165{
166public :
167 wxMimeTypesManagerImpl() { }
168
169 // implement containing class functions
170 wxFileType *GetFileTypeFromExtension(const wxString& ext);
171 wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
172
696e1ea0 173 size_t EnumAllFileTypes(wxArrayString& mimetypes);
1b986aef 174
7c74e7fe
SC
175 // this are NOPs under MacOS
176 bool ReadMailcap(const wxString& filename, bool fallback = TRUE) { return TRUE; }
177 bool ReadMimeTypes(const wxString& filename) { return TRUE; }
178
179 void AddFallback(const wxFileTypeInfo& ft) { m_fallbacks.Add(ft); }
180
181private:
182 wxArrayFileTypeInfo m_fallbacks;
183};
184
185class wxFileTypeImpl
186{
187public:
188 // initialize us with our file type name
189 void SetFileType(const wxString& strFileType)
190 { m_strFileType = strFileType; }
191 void SetExt(const wxString& ext)
192 { m_ext = ext; }
193
194 // implement accessor functions
195 bool GetExtensions(wxArrayString& extensions);
196 bool GetMimeType(wxString *mimeType) const;
197 bool GetIcon(wxIcon *icon) const;
198 bool GetDescription(wxString *desc) const;
199 bool GetOpenCommand(wxString *openCmd,
200 const wxFileType::MessageParameters&) const
201 { return GetCommand(openCmd, "open"); }
202 bool GetPrintCommand(wxString *printCmd,
203 const wxFileType::MessageParameters&) const
204 { return GetCommand(printCmd, "print"); }
205
206private:
207 // helper function
208 bool GetCommand(wxString *command, const char *verb) const;
209
210 wxString m_strFileType, m_ext;
211};
212
b13d92d1
VZ
213#else // Unix
214
215// this class uses both mailcap and mime.types to gather information about file
216// types.
217//
218// The information about mailcap file was extracted from metamail(1) sources and
219// documentation.
220//
221// Format of mailcap file: spaces are ignored, each line is either a comment
222// (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
223// A backslash can be used to quote semicolons and newlines (and, in fact,
224// anything else including itself).
225//
226// The first field is always the MIME type in the form of type/subtype (see RFC
227// 822) where subtype may be '*' meaning "any". Following metamail, we accept
228// "type" which means the same as "type/*", although I'm not sure whether this
229// is standard.
230//
231// The second field is always the command to run. It is subject to
232// parameter/filename expansion described below.
233//
234// All the following fields are optional and may not be present at all. If
235// they're present they may appear in any order, although each of them should
236// appear only once. The optional fields are the following:
237// * notes=xxx is an uninterpreted string which is silently ignored
238// * test=xxx is the command to be used to determine whether this mailcap line
239// applies to our data or not. The RHS of this field goes through the
240// parameter/filename expansion (as the 2nd field) and the resulting string
241// is executed. The line applies only if the command succeeds, i.e. returns 0
242// exit code.
243// * print=xxx is the command to be used to print (and not view) the data of
244// this type (parameter/filename expansion is done here too)
245// * edit=xxx is the command to open/edit the data of this type
246// * needsterminal means that a new console must be created for the viewer
247// * copiousoutput means that the viewer doesn't interact with the user but
248// produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
249// good example), thus it might be a good idea to use some kind of paging
250// mechanism.
251// * textualnewlines means not to perform CR/LF translation (not honored)
252// * compose and composetyped fields are used to determine the program to be
253// called to create a new message pert in the specified format (unused).
254//
ec4768ef 255// Parameter/filename xpansion:
b13d92d1
VZ
256// * %s is replaced with the (full) file name
257// * %t is replaced with MIME type/subtype of the entry
258// * for multipart type only %n is replaced with the nnumber of parts and %F is
259// replaced by an array of (content-type, temporary file name) pairs for all
260// message parts (TODO)
261// * %{parameter} is replaced with the value of parameter taken from
262// Content-type header line of the message.
263//
264// FIXME any docs with real descriptions of these files??
265//
266// There are 2 possible formats for mime.types file, one entry per line (used
267// for global mime.types) and "expanded" format where an entry takes multiple
268// lines (used for users mime.types).
269//
270// For both formats spaces are ignored and lines starting with a '#' are
271// comments. Each record has one of two following forms:
272// a) for "brief" format:
273// <mime type> <space separated list of extensions>
ec4768ef 274// b) for "expanded" format:
b13d92d1
VZ
275// type=<mime type> \ desc="<description>" \ exts="ext"
276//
277// We try to autodetect the format of mime.types: if a non-comment line starts
278// with "type=" we assume the second format, otherwise the first one.
279
280// there may be more than one entry for one and the same mime type, to
281// choose the right one we have to run the command specified in the test
282// field on our data.
283class MailCapEntry
284{
285public:
286 // ctor
287 MailCapEntry(const wxString& openCmd,
288 const wxString& printCmd,
289 const wxString& testCmd)
290 : m_openCmd(openCmd), m_printCmd(printCmd), m_testCmd(testCmd)
291 {
292 m_next = NULL;
293 }
294
295 // accessors
296 const wxString& GetOpenCmd() const { return m_openCmd; }
297 const wxString& GetPrintCmd() const { return m_printCmd; }
298 const wxString& GetTestCmd() const { return m_testCmd; }
299
300 MailCapEntry *GetNext() const { return m_next; }
301
302 // operations
303 // prepend this element to the list
304 void Prepend(MailCapEntry *next) { m_next = next; }
cc385968
VZ
305 // insert into the list at given position
306 void Insert(MailCapEntry *next, size_t pos)
307 {
308 // FIXME slooow...
309 MailCapEntry *cur;
310 size_t n = 0;
311 for ( cur = next; cur != NULL; cur = cur->m_next, n++ ) {
312 if ( n == pos )
313 break;
314 }
315
223d09f6 316 wxASSERT_MSG( n == pos, wxT("invalid position in MailCapEntry::Insert") );
cc385968
VZ
317
318 m_next = cur->m_next;
319 cur->m_next = this;
320 }
321 // append this element to the list
b13d92d1
VZ
322 void Append(MailCapEntry *next)
323 {
223d09f6 324 wxCHECK_RET( next != NULL, wxT("Append()ing to what?") );
cc385968 325
b13d92d1
VZ
326 // FIXME slooow...
327 MailCapEntry *cur;
328 for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
329 ;
330
331 cur->m_next = this;
332
223d09f6 333 wxASSERT_MSG( !m_next, wxT("Append()ing element already in the list?") );
b13d92d1
VZ
334 }
335
336private:
337 wxString m_openCmd, // command to use to open/view the file
338 m_printCmd, // print
339 m_testCmd; // only apply this entry if test yields
340 // true (i.e. the command returns 0)
341
342 MailCapEntry *m_next; // in the linked list
343};
344
345WX_DEFINE_ARRAY(MailCapEntry *, ArrayTypeEntries);
346
b9517a0a
VZ
347// the base class which may be used to find an icon for the MIME type
348class wxMimeTypeIconHandler
349{
350public:
351 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon) = 0;
352};
353
354WX_DEFINE_ARRAY(wxMimeTypeIconHandler *, ArrayIconHandlers);
355
356// the icon handler which uses GNOME MIME database
357class wxGNOMEIconHandler : public wxMimeTypeIconHandler
358{
359public:
360 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon);
361
362private:
363 void Init();
364 void LoadIconsFromKeyFile(const wxString& filename);
365 void LoadKeyFilesFromDir(const wxString& dirbase);
366
367 static bool m_inited;
368
369 static wxSortedArrayString ms_mimetypes;
370 static wxArrayString ms_icons;
371};
372
373// the icon handler which uses KDE MIME database
374class wxKDEIconHandler : public wxMimeTypeIconHandler
375{
376public:
377 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon);
378
379private:
380 void LoadLinksForMimeSubtype(const wxString& dirbase,
381 const wxString& subdir,
382 const wxString& filename);
383 void LoadLinksForMimeType(const wxString& dirbase,
384 const wxString& subdir);
385 void LoadLinkFilesFromDir(const wxString& dirbase);
386 void Init();
387
388 static bool m_inited;
389
390 static wxSortedArrayString ms_mimetypes;
391 static wxArrayString ms_icons;
392};
393
394// this is the real wxMimeTypesManager for Unix
b13d92d1
VZ
395class wxMimeTypesManagerImpl
396{
397friend class wxFileTypeImpl; // give it access to m_aXXX variables
398
399public:
400 // ctor loads all info into memory for quicker access later on
cc385968 401 // TODO it would be nice to load them all, but parse on demand only...
b13d92d1
VZ
402 wxMimeTypesManagerImpl();
403
404 // implement containing class functions
405 wxFileType *GetFileTypeFromExtension(const wxString& ext);
406 wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
407
696e1ea0 408 size_t EnumAllFileTypes(wxArrayString& mimetypes);
1b986aef 409
cc385968
VZ
410 bool ReadMailcap(const wxString& filename, bool fallback = FALSE);
411 bool ReadMimeTypes(const wxString& filename);
b13d92d1 412
8e124873
VZ
413 void AddFallback(const wxFileTypeInfo& filetype);
414
415 // add information about the given mimetype
416 void AddMimeTypeInfo(const wxString& mimetype,
417 const wxString& extensions,
418 const wxString& description);
419 void AddMailcapInfo(const wxString& strType,
420 const wxString& strOpenCmd,
421 const wxString& strPrintCmd,
422 const wxString& strTest,
423 const wxString& strDesc);
424
b13d92d1
VZ
425 // accessors
426 // get the string containing space separated extensions for the given
427 // file type
428 wxString GetExtension(size_t index) { return m_aExtensions[index]; }
429
b9517a0a
VZ
430 // get the array of icon handlers
431 static ArrayIconHandlers& GetIconHandlers();
432
b13d92d1
VZ
433private:
434 wxArrayString m_aTypes, // MIME types
435 m_aDescriptions, // descriptions (just some text)
436 m_aExtensions; // space separated list of extensions
437 ArrayTypeEntries m_aEntries; // commands and tests for this file type
b9517a0a
VZ
438
439 // head of the linked list of the icon handlers
440 static ArrayIconHandlers ms_iconHandlers;
b13d92d1
VZ
441};
442
443class wxFileTypeImpl
444{
445public:
446 // initialization functions
447 void Init(wxMimeTypesManagerImpl *manager, size_t index)
448 { m_manager = manager; m_index = index; }
449
450 // accessors
451 bool GetExtensions(wxArrayString& extensions);
452 bool GetMimeType(wxString *mimeType) const
453 { *mimeType = m_manager->m_aTypes[m_index]; return TRUE; }
b9517a0a 454 bool GetIcon(wxIcon *icon) const;
b13d92d1
VZ
455 bool GetDescription(wxString *desc) const
456 { *desc = m_manager->m_aDescriptions[m_index]; return TRUE; }
457
458 bool GetOpenCommand(wxString *openCmd,
459 const wxFileType::MessageParameters& params) const
460 {
461 return GetExpandedCommand(openCmd, params, TRUE);
462 }
463
464 bool GetPrintCommand(wxString *printCmd,
465 const wxFileType::MessageParameters& params) const
466 {
467 return GetExpandedCommand(printCmd, params, FALSE);
468 }
469
470private:
471 // get the entry which passes the test (may return NULL)
472 MailCapEntry *GetEntry(const wxFileType::MessageParameters& params) const;
473
474 // choose the correct entry to use and expand the command
475 bool GetExpandedCommand(wxString *expandedCmd,
476 const wxFileType::MessageParameters& params,
477 bool open) const;
478
479 wxMimeTypesManagerImpl *m_manager;
480 size_t m_index; // in the wxMimeTypesManagerImpl arrays
481};
482
483#endif // OS type
484
8e124873
VZ
485// ============================================================================
486// common classes
487// ============================================================================
488
489// ----------------------------------------------------------------------------
490// wxFileTypeInfo
491// ----------------------------------------------------------------------------
492
493wxFileTypeInfo::wxFileTypeInfo(const char *mimeType,
494 const char *openCmd,
495 const char *printCmd,
496 const char *desc,
497 ...)
498 : m_mimeType(mimeType),
499 m_openCmd(openCmd),
500 m_printCmd(printCmd),
501 m_desc(desc)
502{
503 va_list argptr;
504 va_start(argptr, desc);
505
506 for ( ;; )
507 {
508 const char *ext = va_arg(argptr, const char *);
509 if ( !ext )
510 {
511 // NULL terminates the list
512 break;
513 }
514
515 m_exts.Add(ext);
516 }
517
518 va_end(argptr);
519}
520
b13d92d1
VZ
521// ============================================================================
522// implementation of the wrapper classes
523// ============================================================================
524
525// ----------------------------------------------------------------------------
526// wxFileType
527// ----------------------------------------------------------------------------
528
529wxString wxFileType::ExpandCommand(const wxString& command,
530 const wxFileType::MessageParameters& params)
531{
532 bool hasFilename = FALSE;
533
534 wxString str;
223d09f6
KB
535 for ( const wxChar *pc = command.c_str(); *pc != wxT('\0'); pc++ ) {
536 if ( *pc == wxT('%') ) {
b13d92d1 537 switch ( *++pc ) {
223d09f6 538 case wxT('s'):
b13d92d1
VZ
539 // '%s' expands into file name (quoted because it might
540 // contain spaces) - except if there are already quotes
341c92a8
VZ
541 // there because otherwise some programs may get confused
542 // by double double quotes
b13d92d1 543#if 0
223d09f6 544 if ( *(pc - 2) == wxT('"') )
b13d92d1
VZ
545 str << params.GetFileName();
546 else
223d09f6 547 str << wxT('"') << params.GetFileName() << wxT('"');
b13d92d1
VZ
548#endif
549 str << params.GetFileName();
550 hasFilename = TRUE;
551 break;
552
223d09f6 553 case wxT('t'):
b13d92d1
VZ
554 // '%t' expands into MIME type (quote it too just to be
555 // consistent)
223d09f6 556 str << wxT('\'') << params.GetMimeType() << wxT('\'');
b13d92d1
VZ
557 break;
558
223d09f6 559 case wxT('{'):
b13d92d1 560 {
223d09f6 561 const wxChar *pEnd = wxStrchr(pc, wxT('}'));
b13d92d1
VZ
562 if ( pEnd == NULL ) {
563 wxString mimetype;
564 wxLogWarning(_("Unmatched '{' in an entry for "
565 "mime type %s."),
566 params.GetMimeType().c_str());
223d09f6 567 str << wxT("%{");
b13d92d1
VZ
568 }
569 else {
570 wxString param(pc + 1, pEnd - pc - 1);
223d09f6 571 str << wxT('\'') << params.GetParamValue(param) << wxT('\'');
b13d92d1
VZ
572 pc = pEnd;
573 }
574 }
575 break;
576
223d09f6
KB
577 case wxT('n'):
578 case wxT('F'):
b13d92d1
VZ
579 // TODO %n is the number of parts, %F is an array containing
580 // the names of temp files these parts were written to
581 // and their mime types.
582 break;
583
584 default:
223d09f6 585 wxLogDebug(wxT("Unknown field %%%c in command '%s'."),
b13d92d1
VZ
586 *pc, command.c_str());
587 str << *pc;
588 }
589 }
590 else {
591 str << *pc;
592 }
593 }
594
595 // metamail(1) man page states that if the mailcap entry doesn't have '%s'
596 // the program will accept the data on stdin: so give it to it!
597 if ( !hasFilename && !str.IsEmpty() ) {
223d09f6 598 str << wxT(" < '") << params.GetFileName() << wxT('\'');
b13d92d1
VZ
599 }
600
601 return str;
602}
603
604wxFileType::wxFileType()
605{
606 m_impl = new wxFileTypeImpl;
607}
608
609wxFileType::~wxFileType()
610{
611 delete m_impl;
612}
613
614bool wxFileType::GetExtensions(wxArrayString& extensions)
615{
616 return m_impl->GetExtensions(extensions);
617}
618
619bool wxFileType::GetMimeType(wxString *mimeType) const
620{
621 return m_impl->GetMimeType(mimeType);
622}
623
624bool wxFileType::GetIcon(wxIcon *icon) const
625{
626 return m_impl->GetIcon(icon);
627}
628
629bool wxFileType::GetDescription(wxString *desc) const
630{
631 return m_impl->GetDescription(desc);
632}
633
634bool
635wxFileType::GetOpenCommand(wxString *openCmd,
636 const wxFileType::MessageParameters& params) const
637{
638 return m_impl->GetOpenCommand(openCmd, params);
639}
640
641bool
642wxFileType::GetPrintCommand(wxString *printCmd,
643 const wxFileType::MessageParameters& params) const
644{
645 return m_impl->GetPrintCommand(printCmd, params);
646}
647
648// ----------------------------------------------------------------------------
649// wxMimeTypesManager
650// ----------------------------------------------------------------------------
651
a5a19b83
VZ
652bool wxMimeTypesManager::IsOfType(const wxString& mimeType,
653 const wxString& wildcard)
654{
223d09f6
KB
655 wxASSERT_MSG( mimeType.Find(wxT('*')) == wxNOT_FOUND,
656 wxT("first MIME type can't contain wildcards") );
a5a19b83
VZ
657
658 // all comparaisons are case insensitive (2nd arg of IsSameAs() is FALSE)
223d09f6 659 if ( wildcard.BeforeFirst(wxT('/')).IsSameAs(mimeType.BeforeFirst(wxT('/')), FALSE) )
a5a19b83 660 {
223d09f6 661 wxString strSubtype = wildcard.AfterFirst(wxT('/'));
a5a19b83 662
223d09f6
KB
663 if ( strSubtype == wxT("*") ||
664 strSubtype.IsSameAs(mimeType.AfterFirst(wxT('/')), FALSE) )
a5a19b83
VZ
665 {
666 // matches (either exactly or it's a wildcard)
667 return TRUE;
668 }
669 }
670
671 return FALSE;
672}
673
b13d92d1
VZ
674wxMimeTypesManager::wxMimeTypesManager()
675{
676 m_impl = new wxMimeTypesManagerImpl;
677}
678
679wxMimeTypesManager::~wxMimeTypesManager()
680{
681 delete m_impl;
682}
683
684wxFileType *
685wxMimeTypesManager::GetFileTypeFromExtension(const wxString& ext)
686{
687 return m_impl->GetFileTypeFromExtension(ext);
688}
689
690wxFileType *
691wxMimeTypesManager::GetFileTypeFromMimeType(const wxString& mimeType)
692{
693 return m_impl->GetFileTypeFromMimeType(mimeType);
694}
695
cc385968 696bool wxMimeTypesManager::ReadMailcap(const wxString& filename, bool fallback)
22b4634c 697{
cc385968 698 return m_impl->ReadMailcap(filename, fallback);
22b4634c
VZ
699}
700
cc385968 701bool wxMimeTypesManager::ReadMimeTypes(const wxString& filename)
22b4634c 702{
cc385968 703 return m_impl->ReadMimeTypes(filename);
22b4634c
VZ
704}
705
8e124873
VZ
706void wxMimeTypesManager::AddFallbacks(const wxFileTypeInfo *filetypes)
707{
708 for ( const wxFileTypeInfo *ft = filetypes; ft->IsValid(); ft++ ) {
709 m_impl->AddFallback(*ft);
710 }
711}
712
696e1ea0 713size_t wxMimeTypesManager::EnumAllFileTypes(wxArrayString& mimetypes)
1b986aef 714{
696e1ea0 715 return m_impl->EnumAllFileTypes(mimetypes);
1b986aef
VZ
716}
717
b13d92d1
VZ
718// ============================================================================
719// real (OS specific) implementation
720// ============================================================================
721
722#ifdef __WXMSW__
723
8e124873 724wxString wxFileTypeImpl::GetCommand(const wxChar *verb) const
b13d92d1
VZ
725{
726 // suppress possible error messages
727 wxLogNull nolog;
728 wxString strKey;
b13d92d1 729
c61f4f6d
VZ
730 if ( wxRegKey(wxRegKey::HKCR, m_ext + _T("\\shell")).Exists() )
731 strKey = m_ext;
732 if ( wxRegKey(wxRegKey::HKCR, m_strFileType + _T("\\shell")).Exists() )
733 strKey = m_strFileType;
734
735 if ( !strKey )
736 {
737 // no info
738 return wxEmptyString;
739 }
740
741 strKey << wxT("\\shell\\") << verb << wxT("\\command");
742 wxRegKey key(wxRegKey::HKCR, strKey);
8e124873 743 wxString command;
b13d92d1
VZ
744 if ( key.Open() ) {
745 // it's the default value of the key
223d09f6 746 if ( key.QueryValue(wxT(""), command) ) {
b13d92d1 747 // transform it from '%1' to '%s' style format string
8e124873 748
cc385968
VZ
749 // NB: we don't make any attempt to verify that the string is valid,
750 // i.e. doesn't contain %2, or second %1 or .... But we do make
751 // sure that we return a string with _exactly_ one '%s'!
8e124873
VZ
752 bool foundFilename = FALSE;
753 size_t len = command.Len();
754 for ( size_t n = 0; (n < len) && !foundFilename; n++ ) {
223d09f6
KB
755 if ( command[n] == wxT('%') &&
756 (n + 1 < len) && command[n + 1] == wxT('1') ) {
b13d92d1 757 // replace it with '%s'
223d09f6 758 command[n + 1] = wxT('s');
b13d92d1 759
8e124873 760 foundFilename = TRUE;
b13d92d1
VZ
761 }
762 }
763
8e124873
VZ
764 if ( !foundFilename ) {
765 // we didn't find any '%1'!
766 // HACK: append the filename at the end, hope that it will do
223d09f6 767 command << wxT(" %s");
8e124873 768 }
b13d92d1
VZ
769 }
770 }
c61f4f6d 771 //else: no such file type or no value, will return empty string
b13d92d1 772
8e124873
VZ
773 return command;
774}
775
776bool
777wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
778 const wxFileType::MessageParameters& params)
779 const
780{
781 wxString cmd;
782 if ( m_info ) {
783 cmd = m_info->GetOpenCommand();
784 }
785 else {
223d09f6 786 cmd = GetCommand(wxT("open"));
8e124873
VZ
787 }
788
789 *openCmd = wxFileType::ExpandCommand(cmd, params);
790
791 return !openCmd->IsEmpty();
792}
793
794bool
795wxFileTypeImpl::GetPrintCommand(wxString *printCmd,
796 const wxFileType::MessageParameters& params)
797 const
798{
799 wxString cmd;
800 if ( m_info ) {
801 cmd = m_info->GetPrintCommand();
802 }
803 else {
223d09f6 804 cmd = GetCommand(wxT("print"));
8e124873
VZ
805 }
806
807 *printCmd = wxFileType::ExpandCommand(cmd, params);
808
809 return !printCmd->IsEmpty();
b13d92d1
VZ
810}
811
cc385968 812// TODO this function is half implemented
b13d92d1
VZ
813bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
814{
8e124873
VZ
815 if ( m_info ) {
816 extensions = m_info->GetExtensions();
817
818 return TRUE;
819 }
820 else if ( m_ext.IsEmpty() ) {
b13d92d1
VZ
821 // the only way to get the list of extensions from the file type is to
822 // scan through all extensions in the registry - too slow...
823 return FALSE;
824 }
825 else {
826 extensions.Empty();
827 extensions.Add(m_ext);
828
829 // it's a lie too, we don't return _all_ extensions...
830 return TRUE;
831 }
832}
833
834bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
835{
8e124873
VZ
836 if ( m_info ) {
837 // we already have it
838 *mimeType = m_info->GetMimeType();
839
840 return TRUE;
841 }
842
b13d92d1
VZ
843 // suppress possible error messages
844 wxLogNull nolog;
c61f4f6d 845 wxRegKey key(wxRegKey::HKCR, wxT(".") + m_ext);
223d09f6 846 if ( key.Open() && key.QueryValue(wxT("Content Type"), *mimeType) ) {
b13d92d1
VZ
847 return TRUE;
848 }
849 else {
850 return FALSE;
851 }
852}
853
854bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
855{
b568d04f 856#if wxUSE_GUI
8e124873
VZ
857 if ( m_info ) {
858 // we don't have icons in the fallback resources
859 return FALSE;
860 }
861
b13d92d1 862 wxString strIconKey;
223d09f6 863 strIconKey << m_strFileType << wxT("\\DefaultIcon");
b13d92d1
VZ
864
865 // suppress possible error messages
866 wxLogNull nolog;
867 wxRegKey key(wxRegKey::HKCR, strIconKey);
868
869 if ( key.Open() ) {
870 wxString strIcon;
871 // it's the default value of the key
223d09f6 872 if ( key.QueryValue(wxT(""), strIcon) ) {
b13d92d1
VZ
873 // the format is the following: <full path to file>, <icon index>
874 // NB: icon index may be negative as well as positive and the full
875 // path may contain the environment variables inside '%'
223d09f6
KB
876 wxString strFullPath = strIcon.BeforeLast(wxT(',')),
877 strIndex = strIcon.AfterLast(wxT(','));
b13d92d1 878
3c67202d
VZ
879 // index may be omitted, in which case BeforeLast(',') is empty and
880 // AfterLast(',') is the whole string
b13d92d1
VZ
881 if ( strFullPath.IsEmpty() ) {
882 strFullPath = strIndex;
223d09f6 883 strIndex = wxT("0");
b13d92d1
VZ
884 }
885
886 wxString strExpPath = wxExpandEnvVars(strFullPath);
4de6207a 887 int nIndex = wxAtoi(strIndex);
b13d92d1
VZ
888
889 HICON hIcon = ExtractIcon(GetModuleHandle(NULL), strExpPath, nIndex);
890 switch ( (int)hIcon ) {
891 case 0: // means no icons were found
892 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
223d09f6 893 wxLogDebug(wxT("incorrect registry entry '%s': no such icon."),
b13d92d1
VZ
894 key.GetName().c_str());
895 break;
896
897 default:
898 icon->SetHICON((WXHICON)hIcon);
899 return TRUE;
900 }
901 }
902 }
903
904 // no such file type or no value or incorrect icon entry
b568d04f
VZ
905#endif // wxUSE_GUI
906
b13d92d1
VZ
907 return FALSE;
908}
909
910bool wxFileTypeImpl::GetDescription(wxString *desc) const
911{
8e124873
VZ
912 if ( m_info ) {
913 // we already have it
914 *desc = m_info->GetDescription();
915
916 return TRUE;
917 }
918
b13d92d1
VZ
919 // suppress possible error messages
920 wxLogNull nolog;
921 wxRegKey key(wxRegKey::HKCR, m_strFileType);
922
923 if ( key.Open() ) {
924 // it's the default value of the key
223d09f6 925 if ( key.QueryValue(wxT(""), *desc) ) {
b13d92d1
VZ
926 return TRUE;
927 }
928 }
929
930 return FALSE;
931}
932
933// extension -> file type
934wxFileType *
935wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
936{
937 // add the leading point if necessary
938 wxString str;
223d09f6
KB
939 if ( ext[0u] != wxT('.') ) {
940 str = wxT('.');
b13d92d1
VZ
941 }
942 str << ext;
943
944 // suppress possible error messages
945 wxLogNull nolog;
946
c61f4f6d
VZ
947 bool knownExtension = FALSE;
948
b13d92d1
VZ
949 wxString strFileType;
950 wxRegKey key(wxRegKey::HKCR, str);
951 if ( key.Open() ) {
952 // it's the default value of the key
223d09f6 953 if ( key.QueryValue(wxT(""), strFileType) ) {
b13d92d1
VZ
954 // create the new wxFileType object
955 wxFileType *fileType = new wxFileType;
8e124873
VZ
956 fileType->m_impl->Init(strFileType, ext);
957
958 return fileType;
959 }
c61f4f6d
VZ
960 else {
961 // this extension doesn't have a filetype, but it's known to the
962 // system and may be has some other useful keys (open command or
963 // content-type), so still return a file type object for it
964 knownExtension = TRUE;
965 }
8e124873
VZ
966 }
967
968 // check the fallbacks
969 // TODO linear search is potentially slow, perhaps we should use a sorted
970 // array?
971 size_t count = m_fallbacks.GetCount();
972 for ( size_t n = 0; n < count; n++ ) {
973 if ( m_fallbacks[n].GetExtensions().Index(ext) != wxNOT_FOUND ) {
974 wxFileType *fileType = new wxFileType;
975 fileType->m_impl->Init(m_fallbacks[n]);
b13d92d1
VZ
976
977 return fileType;
978 }
979 }
980
c61f4f6d
VZ
981 if ( knownExtension )
982 {
983 wxFileType *fileType = new wxFileType;
984 fileType->m_impl->Init(wxEmptyString, ext);
985
986 return fileType;
987 }
988 else
989 {
990 // unknown extension
991 return NULL;
992 }
b13d92d1
VZ
993}
994
995// MIME type -> extension -> file type
996wxFileType *
997wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
998{
1b986aef 999 wxString strKey = MIME_DATABASE_KEY;
b13d92d1
VZ
1000 strKey << mimeType;
1001
1002 // suppress possible error messages
1003 wxLogNull nolog;
1004
1005 wxString ext;
1006 wxRegKey key(wxRegKey::HKCR, strKey);
1007 if ( key.Open() ) {
223d09f6 1008 if ( key.QueryValue(wxT("Extension"), ext) ) {
b13d92d1
VZ
1009 return GetFileTypeFromExtension(ext);
1010 }
1011 }
1012
8e124873
VZ
1013 // check the fallbacks
1014 // TODO linear search is potentially slow, perhaps we should use a sorted
1015 // array?
1016 size_t count = m_fallbacks.GetCount();
1017 for ( size_t n = 0; n < count; n++ ) {
1018 if ( wxMimeTypesManager::IsOfType(mimeType,
1019 m_fallbacks[n].GetMimeType()) ) {
1020 wxFileType *fileType = new wxFileType;
1021 fileType->m_impl->Init(m_fallbacks[n]);
1022
1023 return fileType;
1024 }
1025 }
1026
b13d92d1
VZ
1027 // unknown MIME type
1028 return NULL;
1029}
1030
696e1ea0 1031size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
1b986aef
VZ
1032{
1033 // enumerate all keys under MIME_DATABASE_KEY
1034 wxRegKey key(wxRegKey::HKCR, MIME_DATABASE_KEY);
7c74e7fe 1035
696e1ea0
VZ
1036 wxString type;
1037 long cookie;
1038 bool cont = key.GetFirstKey(type, cookie);
1039 while ( cont )
1040 {
1041 mimetypes.Add(type);
1042
1043 cont = key.GetNextKey(type, cookie);
1044 }
1045
1046 return mimetypes.GetCount();
1b986aef
VZ
1047}
1048
1049#elif defined ( __WXMAC__ )
7c74e7fe
SC
1050
1051bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const
1052{
1053 return FALSE;
1054}
1055
1056// @@ this function is half implemented
1057bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
1058{
1059 return FALSE;
1060}
1061
1062bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
1063{
1b986aef
VZ
1064 if ( m_strFileType.Length() > 0 )
1065 {
1066 *mimeType = m_strFileType ;
1067 return TRUE ;
1068 }
1069 else
7c74e7fe
SC
1070 return FALSE;
1071}
1072
1073bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
1074{
1075 // no such file type or no value or incorrect icon entry
1076 return FALSE;
1077}
1078
1079bool wxFileTypeImpl::GetDescription(wxString *desc) const
1080{
1081 return FALSE;
1082}
1083
1084// extension -> file type
1085wxFileType *
1086wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e)
1087{
1b986aef
VZ
1088 wxString ext = e ;
1089 ext = ext.Lower() ;
1090 if ( ext == "txt" )
1091 {
1092 wxFileType *fileType = new wxFileType;
1093 fileType->m_impl->SetFileType("text/text");
1094 fileType->m_impl->SetExt(ext);
1095 return fileType;
1096 }
1097 else if ( ext == "htm" || ext == "html" )
1098 {
1099 wxFileType *fileType = new wxFileType;
1100 fileType->m_impl->SetFileType("text/html");
1101 fileType->m_impl->SetExt(ext);
1102 return fileType;
1103 }
1104 else if ( ext == "gif" )
1105 {
1106 wxFileType *fileType = new wxFileType;
1107 fileType->m_impl->SetFileType("image/gif");
1108 fileType->m_impl->SetExt(ext);
1109 return fileType;
1110 }
1111 else if ( ext == "png" )
1112 {
1113 wxFileType *fileType = new wxFileType;
1114 fileType->m_impl->SetFileType("image/png");
1115 fileType->m_impl->SetExt(ext);
1116 return fileType;
1117 }
1118 else if ( ext == "jpg" || ext == "jpeg" )
1119 {
1120 wxFileType *fileType = new wxFileType;
1121 fileType->m_impl->SetFileType("image/jpeg");
1122 fileType->m_impl->SetExt(ext);
1123 return fileType;
1124 }
1125 else if ( ext == "bmp" )
1126 {
1127 wxFileType *fileType = new wxFileType;
1128 fileType->m_impl->SetFileType("image/bmp");
1129 fileType->m_impl->SetExt(ext);
1130 return fileType;
1131 }
1132 else if ( ext == "tif" || ext == "tiff" )
1133 {
1134 wxFileType *fileType = new wxFileType;
1135 fileType->m_impl->SetFileType("image/tiff");
1136 fileType->m_impl->SetExt(ext);
1137 return fileType;
1138 }
1139 else if ( ext == "xpm" )
1140 {
1141 wxFileType *fileType = new wxFileType;
1142 fileType->m_impl->SetFileType("image/xpm");
1143 fileType->m_impl->SetExt(ext);
1144 return fileType;
1145 }
1146 else if ( ext == "xbm" )
1147 {
1148 wxFileType *fileType = new wxFileType;
1149 fileType->m_impl->SetFileType("image/xbm");
1150 fileType->m_impl->SetExt(ext);
1151 return fileType;
1152 }
1153
1154 // unknown extension
1155 return NULL;
7c74e7fe
SC
1156}
1157
1158// MIME type -> extension -> file type
1159wxFileType *
1160wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
1161{
1162 return NULL;
1163}
1b986aef 1164
696e1ea0 1165size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
1b986aef
VZ
1166{
1167 wxFAIL_MSG( _T("TODO") ); // VZ: don't know anything about this for Mac
1168
1169 return 0;
1170}
1171
b13d92d1
VZ
1172#else // Unix
1173
b9517a0a
VZ
1174// ============================================================================
1175// Unix implementation
1176// ============================================================================
1177
1178// ----------------------------------------------------------------------------
1179// various statics
1180// ----------------------------------------------------------------------------
1181
1182static wxGNOMEIconHandler gs_iconHandlerGNOME;
1183static wxKDEIconHandler gs_iconHandlerKDE;
1184
1185bool wxGNOMEIconHandler::m_inited = FALSE;
1186wxSortedArrayString wxGNOMEIconHandler::ms_mimetypes;
1187wxArrayString wxGNOMEIconHandler::ms_icons;
1188
1189bool wxKDEIconHandler::m_inited = FALSE;
1190wxSortedArrayString wxKDEIconHandler::ms_mimetypes;
1191wxArrayString wxKDEIconHandler::ms_icons;
1192
1193ArrayIconHandlers wxMimeTypesManagerImpl::ms_iconHandlers;
1194
1195// ----------------------------------------------------------------------------
1196// wxGNOMEIconHandler
1197// ----------------------------------------------------------------------------
1198
1199// GNOME stores the info we're interested in in several locations:
1200// 1. xxx.keys files under /usr/share/mime-info
1201// 2. xxx.keys files under ~/.gnome/mime-info
1202//
1203// The format of xxx.keys file is the following:
1204//
1205// mimetype/subtype:
1206// field=value
1207//
1208// with blank lines separating the entries and indented lines starting with
1209// TABs. We're interested in the field icon-filename whose value is the path
1210// containing the icon.
1211
1212void wxGNOMEIconHandler::LoadIconsFromKeyFile(const wxString& filename)
1213{
1214 wxTextFile textfile(filename);
1215 if ( !textfile.Open() )
1216 return;
1217
1218 // values for the entry being parsed
1219 wxString curMimeType, curIconFile;
1220
1221 const wxChar *pc;
1222 size_t nLineCount = textfile.GetLineCount();
1223 for ( size_t nLine = 0; ; nLine++ )
1224 {
1225 if ( nLine < nLineCount )
1226 {
1227 pc = textfile[nLine].c_str();
1228 if ( *pc == _T('#') )
1229 {
1230 // skip comments
1231 continue;
1232 }
1233 }
1234 else
1235 {
1236 // so that we will fall into the "if" below
1237 pc = NULL;
1238 }
1239
1240 if ( !pc || !*pc )
1241 {
1242 // end of the entry
1243 if ( !!curMimeType && !!curIconFile )
1244 {
1245 // do we already know this mimetype?
1246 int i = ms_mimetypes.Index(curMimeType);
1247 if ( i == wxNOT_FOUND )
1248 {
1249 // add a new entry
1250 size_t n = ms_mimetypes.Add(curMimeType);
1251 ms_icons.Insert(curIconFile, n);
1252 }
1253 else
1254 {
1255 // replace the existing one (this means that the directories
1256 // should be searched in order of increased priority!)
1257 ms_icons[(size_t)i] = curIconFile;
1258 }
1259 }
1260
1261 if ( !pc )
1262 {
1263 // the end - this can only happen if nLine == nLineCount
1264 break;
1265 }
1266
1267 curIconFile.Empty();
1268
1269 continue;
1270 }
1271
1272 // what do we have here?
1273 if ( *pc == _T('\t') )
1274 {
1275 // this is a field=value ling
1276 pc++; // skip leading TAB
1277
1278 static const int lenField = 13; // strlen("icon-filename")
1279 if ( wxStrncmp(pc, _T("icon-filename"), lenField) == 0 )
1280 {
1281 // skip '=' which follows and take everything left until the end
1282 // of line
1283 curIconFile = pc + lenField + 1;
1284 }
1285 //else: some other field, we don't care
1286 }
1287 else
1288 {
1289 // this is the start of the new section
1290 curMimeType.Empty();
1291
1292 while ( *pc != _T(':') && *pc != _T('\0') )
1293 {
1294 curMimeType += *pc++;
1295 }
1296
1297 if ( !*pc )
1298 {
1299 // we reached the end of line without finding the colon,
1300 // something is wrong - ignore this line completely
1301 wxLogDebug(_T("Unreckognized line %d in file '%s' ignored"),
1302 nLine + 1, filename.c_str());
1303
1304 break;
1305 }
1306 }
1307 }
1308}
1309
1310void wxGNOMEIconHandler::LoadKeyFilesFromDir(const wxString& dirbase)
1311{
1312 wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
1313 _T("base directory shouldn't end with a slash") );
1314
1315 wxString dirname = dirbase;
1316 dirname << _T("/mime-info");
1317
1318 if ( !wxDir::Exists(dirname) )
1319 return;
1320
1321 wxDir dir(dirname);
1322 if ( !dir.IsOpened() )
1323 return;
1324
1325 // we will concatenate it with filename to get the full path below
1326 dirname += _T('/');
1327
1328 wxString filename;
1329 bool cont = dir.GetFirst(&filename, _T("*.keys"), wxDIR_FILES);
1330 while ( cont )
1331 {
1332 LoadIconsFromKeyFile(dirname + filename);
1333
1334 cont = dir.GetNext(&filename);
1335 }
1336}
1337
1338void wxGNOMEIconHandler::Init()
1339{
1340 wxArrayString dirs;
1341 dirs.Add(_T("/usr/share"));
fb5ddb4c 1342
af159c44
RR
1343 wxString gnomedir;
1344 wxGetHomeDir( &gnomedir );
1345 gnomedir += _T("/.gnome");
1346 dirs.Add( gnomedir );
b9517a0a
VZ
1347
1348 size_t nDirs = dirs.GetCount();
1349 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
1350 {
1351 LoadKeyFilesFromDir(dirs[nDir]);
1352 }
1353
1354 m_inited = TRUE;
1355}
1356
1357bool wxGNOMEIconHandler::GetIcon(const wxString& mimetype, wxIcon *icon)
1358{
1359 if ( !m_inited )
1360 {
1361 Init();
1362 }
1363
1364 int index = ms_mimetypes.Index(mimetype);
1365 if ( index == wxNOT_FOUND )
1366 return FALSE;
1367
1368 wxString iconname = ms_icons[(size_t)index];
1369
1370#if wxUSE_GUI
1371 *icon = wxIcon(iconname);
1372#else
1373 // helpful for testing in console mode
1374 wxLogDebug(_T("Found GNOME icon for '%s': '%s'\n"),
1375 mimetype.c_str(), iconname.c_str());
1376#endif
1377
1378 return TRUE;
1379}
1380
1381// ----------------------------------------------------------------------------
1382// wxKDEIconHandler
1383// ----------------------------------------------------------------------------
1384
1385// KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype
1386// may be found in either of the following locations
1387//
509a6196 1388// 1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
b9517a0a
VZ
1389// 2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
1390//
1391// The format of a .kdelnk file is almost the same as the one used by
1392// wxFileConfig, i.e. there are groups, comments and entries. The icon is the
1393// value for the entry "Type"
1394
1395void wxKDEIconHandler::LoadLinksForMimeSubtype(const wxString& dirbase,
1396 const wxString& subdir,
1397 const wxString& filename)
1398{
1399 wxFFile file(dirbase + filename);
1400 if ( !file.IsOpened() )
1401 return;
1402
1403 // these files are small, slurp the entire file at once
1404 wxString text;
1405 if ( !file.ReadAll(&text) )
1406 return;
1407
1408 int pos = text.Find(_T("Icon="));
1409 if ( pos == wxNOT_FOUND )
1410 {
1411 // no icon info
1412 return;
1413 }
1414
1415 wxString icon;
1416
1417 const wxChar *pc = text.c_str() + pos + 5; // 5 == strlen("Icon=")
1418 while ( *pc && *pc != _T('\n') )
1419 {
1420 icon += *pc++;
1421 }
1422
1423 if ( !!icon )
1424 {
1425 // don't check that the file actually exists - would be too slow
1426 icon.Prepend(_T("/usr/share/icons/"));
1427
1428 // construct mimetype from the directory name and the basename of the
1429 // file (it always has .kdelnk extension)
1430 wxString mimetype;
1431 mimetype << subdir << _T('/') << filename.BeforeLast(_T('.'));
1432
1433 // do we already have this MIME type?
1434 int i = ms_mimetypes.Index(mimetype);
1435 if ( i == wxNOT_FOUND )
1436 {
1437 // add it
1438 size_t n = ms_mimetypes.Add(mimetype);
1439 ms_icons.Insert(icon, n);
1440 }
1441 else
1442 {
1443 // replace the old value
1444 ms_icons[(size_t)i] = icon;
1445 }
1446 }
1447}
1448
1449void wxKDEIconHandler::LoadLinksForMimeType(const wxString& dirbase,
1450 const wxString& subdir)
1451{
1452 wxString dirname = dirbase;
1453 dirname += subdir;
1454 wxDir dir(dirname);
1455 if ( !dir.IsOpened() )
1456 return;
1457
1458 dirname += _T('/');
1459
1460 wxString filename;
1461 bool cont = dir.GetFirst(&filename, _T("*.kdelnk"), wxDIR_FILES);
1462 while ( cont )
1463 {
1464 LoadLinksForMimeSubtype(dirname, subdir, filename);
1465
1466 cont = dir.GetNext(&filename);
1467 }
1468}
1469
1470void wxKDEIconHandler::LoadLinkFilesFromDir(const wxString& dirbase)
1471{
1472 wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
1473 _T("base directory shouldn't end with a slash") );
1474
1475 wxString dirname = dirbase;
1476 dirname << _T("/mimelnk");
1477
1478 if ( !wxDir::Exists(dirname) )
1479 return;
1480
1481 wxDir dir(dirname);
1482 if ( !dir.IsOpened() )
1483 return;
1484
1485 // we will concatenate it with dir name to get the full path below
1486 dirname += _T('/');
1487
1488 wxString subdir;
1489 bool cont = dir.GetFirst(&subdir, wxEmptyString, wxDIR_DIRS);
1490 while ( cont )
1491 {
1492 LoadLinksForMimeType(dirname, subdir);
1493
1494 cont = dir.GetNext(&subdir);
1495 }
1496}
1497
1498void wxKDEIconHandler::Init()
1499{
1500 wxArrayString dirs;
509a6196
VZ
1501
1502 // the variable KDEDIR is set when KDE is running
1503 const char *kdedir = getenv("KDEDIR");
1504 if ( kdedir )
1505 {
1506 dirs.Add(wxString(kdedir) + _T("/share"));
1507 }
1508 else
1509 {
1510 // try to guess KDEDIR
1511 dirs.Add(_T("/usr/share"));
1512 dirs.Add(_T("/opt/kde/share"));
1513 }
1514
1515 dirs.Add(wxGetHomeDir() + _T("/.kde/share"));
b9517a0a
VZ
1516
1517 size_t nDirs = dirs.GetCount();
1518 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
1519 {
1520 LoadLinkFilesFromDir(dirs[nDir]);
1521 }
1522
1523 m_inited = TRUE;
1524}
1525
1526bool wxKDEIconHandler::GetIcon(const wxString& mimetype, wxIcon *icon)
1527{
1528 if ( !m_inited )
1529 {
1530 Init();
1531 }
1532
1533 int index = ms_mimetypes.Index(mimetype);
1534 if ( index == wxNOT_FOUND )
1535 return FALSE;
1536
1537 wxString iconname = ms_icons[(size_t)index];
1538
1539#if wxUSE_GUI
1540 *icon = wxIcon(iconname);
1541#else
1542 // helpful for testing in console mode
1543 wxLogDebug(_T("Found KDE icon for '%s': '%s'\n"),
1544 mimetype.c_str(), iconname.c_str());
1545#endif
1546
1547 return TRUE;
1548}
1549
1550// ----------------------------------------------------------------------------
1551// wxFileTypeImpl (Unix)
1552// ----------------------------------------------------------------------------
1553
b13d92d1
VZ
1554MailCapEntry *
1555wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters& params) const
1556{
1557 wxString command;
1558 MailCapEntry *entry = m_manager->m_aEntries[m_index];
1559 while ( entry != NULL ) {
cc385968 1560 // notice that an empty command would always succeed (it's ok)
b13d92d1
VZ
1561 command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
1562
50920146 1563 if ( command.IsEmpty() || (wxSystem(command) == 0) ) {
b13d92d1 1564 // ok, passed
223d09f6 1565 wxLogTrace(wxT("Test '%s' for mime type '%s' succeeded."),
b13d92d1
VZ
1566 command.c_str(), params.GetMimeType().c_str());
1567 break;
1568 }
1569 else {
223d09f6 1570 wxLogTrace(wxT("Test '%s' for mime type '%s' failed."),
b13d92d1
VZ
1571 command.c_str(), params.GetMimeType().c_str());
1572 }
1573
1574 entry = entry->GetNext();
1575 }
1576
1577 return entry;
1578}
1579
b9517a0a
VZ
1580bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
1581{
1582 wxString mimetype;
1583 (void)GetMimeType(&mimetype);
1584
1585 ArrayIconHandlers& handlers = m_manager->GetIconHandlers();
1586 size_t count = handlers.GetCount();
1587 for ( size_t n = 0; n < count; n++ )
1588 {
1589 if ( handlers[n]->GetIcon(mimetype, icon) )
1590 return TRUE;
1591 }
1592
1593 return FALSE;
1594}
1595
b13d92d1
VZ
1596bool
1597wxFileTypeImpl::GetExpandedCommand(wxString *expandedCmd,
1598 const wxFileType::MessageParameters& params,
1599 bool open) const
1600{
1601 MailCapEntry *entry = GetEntry(params);
1602 if ( entry == NULL ) {
1603 // all tests failed...
1604 return FALSE;
1605 }
1606
1607 wxString cmd = open ? entry->GetOpenCmd() : entry->GetPrintCmd();
1608 if ( cmd.IsEmpty() ) {
1609 // may happen, especially for "print"
1610 return FALSE;
1611 }
1612
1613 *expandedCmd = wxFileType::ExpandCommand(cmd, params);
1614 return TRUE;
1615}
1616
1617bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
1618{
1619 wxString strExtensions = m_manager->GetExtension(m_index);
1620 extensions.Empty();
1621
1622 // one extension in the space or comma delimitid list
1623 wxString strExt;
50920146 1624 for ( const wxChar *p = strExtensions; ; p++ ) {
223d09f6 1625 if ( *p == wxT(' ') || *p == wxT(',') || *p == wxT('\0') ) {
b13d92d1
VZ
1626 if ( !strExt.IsEmpty() ) {
1627 extensions.Add(strExt);
1628 strExt.Empty();
1629 }
1630 //else: repeated spaces (shouldn't happen, but it's not that
1631 // important if it does happen)
1632
223d09f6 1633 if ( *p == wxT('\0') )
b13d92d1
VZ
1634 break;
1635 }
223d09f6 1636 else if ( *p == wxT('.') ) {
b13d92d1
VZ
1637 // remove the dot from extension (but only if it's the first char)
1638 if ( !strExt.IsEmpty() ) {
223d09f6 1639 strExt += wxT('.');
b13d92d1
VZ
1640 }
1641 //else: no, don't append it
1642 }
1643 else {
1644 strExt += *p;
1645 }
1646 }
1647
1648 return TRUE;
1649}
1650
b9517a0a
VZ
1651// ----------------------------------------------------------------------------
1652// wxMimeTypesManagerImpl (Unix)
1653// ----------------------------------------------------------------------------
1654
1655/* static */
1656ArrayIconHandlers& wxMimeTypesManagerImpl::GetIconHandlers()
1657{
1658 if ( ms_iconHandlers.GetCount() == 0 )
1659 {
1660 ms_iconHandlers.Add(&gs_iconHandlerGNOME);
1661 ms_iconHandlers.Add(&gs_iconHandlerKDE);
1662 }
1663
1664 return ms_iconHandlers;
1665}
1666
b13d92d1
VZ
1667// read system and user mailcaps (TODO implement mime.types support)
1668wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
1669{
1670 // directories where we look for mailcap and mime.types by default
1671 // (taken from metamail(1) sources)
50920146 1672 static const wxChar *aStandardLocations[] =
b13d92d1 1673 {
223d09f6
KB
1674 wxT("/etc"),
1675 wxT("/usr/etc"),
1676 wxT("/usr/local/etc"),
1677 wxT("/etc/mail"),
1678 wxT("/usr/public/lib")
b13d92d1
VZ
1679 };
1680
1681 // first read the system wide file(s)
1682 for ( size_t n = 0; n < WXSIZEOF(aStandardLocations); n++ ) {
1683 wxString dir = aStandardLocations[n];
1684
223d09f6 1685 wxString file = dir + wxT("/mailcap");
b13d92d1
VZ
1686 if ( wxFile::Exists(file) ) {
1687 ReadMailcap(file);
1688 }
1689
223d09f6 1690 file = dir + wxT("/mime.types");
b13d92d1
VZ
1691 if ( wxFile::Exists(file) ) {
1692 ReadMimeTypes(file);
1693 }
1694 }
1695
223d09f6 1696 wxString strHome = wxGetenv(wxT("HOME"));
b13d92d1
VZ
1697
1698 // and now the users mailcap
223d09f6 1699 wxString strUserMailcap = strHome + wxT("/.mailcap");
b13d92d1
VZ
1700 if ( wxFile::Exists(strUserMailcap) ) {
1701 ReadMailcap(strUserMailcap);
1702 }
1703
1704 // read the users mime.types
223d09f6 1705 wxString strUserMimeTypes = strHome + wxT("/.mime.types");
b13d92d1
VZ
1706 if ( wxFile::Exists(strUserMimeTypes) ) {
1707 ReadMimeTypes(strUserMimeTypes);
1708 }
1709}
1710
1711wxFileType *
1712wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
1713{
1ee17e1c
VZ
1714 size_t count = m_aExtensions.GetCount();
1715 for ( size_t n = 0; n < count; n++ ) {
1716 wxString extensions = m_aExtensions[n];
1717 while ( !extensions.IsEmpty() ) {
223d09f6
KB
1718 wxString field = extensions.BeforeFirst(wxT(' '));
1719 extensions = extensions.AfterFirst(wxT(' '));
1ee17e1c
VZ
1720
1721 // consider extensions as not being case-sensitive
ec4768ef 1722 if ( field.IsSameAs(ext, FALSE /* no case */) ) {
1ee17e1c
VZ
1723 // found
1724 wxFileType *fileType = new wxFileType;
1725 fileType->m_impl->Init(this, n);
ec4768ef 1726
1ee17e1c
VZ
1727 return fileType;
1728 }
1729 }
1730 }
b13d92d1 1731
1ee17e1c 1732 // not found
b13d92d1
VZ
1733 return NULL;
1734}
1735
1736wxFileType *
1737wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
1738{
1739 // mime types are not case-sensitive
1740 wxString mimetype(mimeType);
1741 mimetype.MakeLower();
1742
1743 // first look for an exact match
1744 int index = m_aTypes.Index(mimetype);
3c67202d 1745 if ( index == wxNOT_FOUND ) {
b13d92d1 1746 // then try to find "text/*" as match for "text/plain" (for example)
3c67202d
VZ
1747 // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
1748 // the whole string - ok.
223d09f6 1749 wxString strCategory = mimetype.BeforeFirst(wxT('/'));
b13d92d1
VZ
1750
1751 size_t nCount = m_aTypes.Count();
1752 for ( size_t n = 0; n < nCount; n++ ) {
223d09f6
KB
1753 if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
1754 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") ) {
b13d92d1
VZ
1755 index = n;
1756 break;
1757 }
1758 }
1759 }
1760
3c67202d 1761 if ( index != wxNOT_FOUND ) {
b13d92d1
VZ
1762 wxFileType *fileType = new wxFileType;
1763 fileType->m_impl->Init(this, index);
1764
1765 return fileType;
1766 }
1767 else {
1768 // not found...
1769 return NULL;
1770 }
1771}
1772
8e124873
VZ
1773void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
1774{
3f1aaa16 1775 wxString extensions;
8e124873
VZ
1776 const wxArrayString& exts = filetype.GetExtensions();
1777 size_t nExts = exts.GetCount();
1778 for ( size_t nExt = 0; nExt < nExts; nExt++ ) {
1779 if ( nExt > 0 ) {
223d09f6 1780 extensions += wxT(' ');
8e124873
VZ
1781 }
1782 extensions += exts[nExt];
1783 }
1784
1785 AddMimeTypeInfo(filetype.GetMimeType(),
1786 extensions,
1787 filetype.GetDescription());
1788
1789 AddMailcapInfo(filetype.GetMimeType(),
1790 filetype.GetOpenCommand(),
1791 filetype.GetPrintCommand(),
223d09f6 1792 wxT(""),
8e124873
VZ
1793 filetype.GetDescription());
1794}
1795
1796void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
1797 const wxString& strExtensions,
1798 const wxString& strDesc)
1799{
1800 int index = m_aTypes.Index(strMimeType);
1801 if ( index == wxNOT_FOUND ) {
1802 // add a new entry
1803 m_aTypes.Add(strMimeType);
1804 m_aEntries.Add(NULL);
1805 m_aExtensions.Add(strExtensions);
1806 m_aDescriptions.Add(strDesc);
1807 }
1808 else {
1809 // modify an existing one
1810 if ( !strDesc.IsEmpty() ) {
1811 m_aDescriptions[index] = strDesc; // replace old value
1812 }
c15d098c 1813 m_aExtensions[index] += ' ' + strExtensions;
8e124873
VZ
1814 }
1815}
1816
1817void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString& strType,
1818 const wxString& strOpenCmd,
1819 const wxString& strPrintCmd,
1820 const wxString& strTest,
1821 const wxString& strDesc)
1822{
1823 MailCapEntry *entry = new MailCapEntry(strOpenCmd, strPrintCmd, strTest);
1824
1825 int nIndex = m_aTypes.Index(strType);
1826 if ( nIndex == wxNOT_FOUND ) {
1827 // new file type
1828 m_aTypes.Add(strType);
1829
1830 m_aEntries.Add(entry);
223d09f6 1831 m_aExtensions.Add(wxT(""));
8e124873
VZ
1832 m_aDescriptions.Add(strDesc);
1833 }
1834 else {
1835 // always append the entry in the tail of the list - info added with
1836 // this function can only come from AddFallbacks()
1837 MailCapEntry *entryOld = m_aEntries[nIndex];
1838 if ( entryOld )
1839 entry->Append(entryOld);
1840 else
1841 m_aEntries[nIndex] = entry;
1842 }
1843}
1844
cc385968 1845bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
b13d92d1 1846{
223d09f6 1847 wxLogTrace(wxT("--- Parsing mime.types file '%s' ---"), strFileName.c_str());
b13d92d1
VZ
1848
1849 wxTextFile file(strFileName);
1850 if ( !file.Open() )
cc385968 1851 return FALSE;
b13d92d1
VZ
1852
1853 // the information we extract
1854 wxString strMimeType, strDesc, strExtensions;
1855
1856 size_t nLineCount = file.GetLineCount();
50920146 1857 const wxChar *pc = NULL;
b13d92d1 1858 for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
22b4634c
VZ
1859 if ( pc == NULL ) {
1860 // now we're at the start of the line
1861 pc = file[nLine].c_str();
1862 }
1863 else {
1864 // we didn't finish with the previous line yet
1865 nLine--;
1866 }
b13d92d1
VZ
1867
1868 // skip whitespace
50920146 1869 while ( wxIsspace(*pc) )
b13d92d1
VZ
1870 pc++;
1871
54acce90
VZ
1872 // comment or blank line?
1873 if ( *pc == wxT('#') || !*pc ) {
22b4634c
VZ
1874 // skip the whole line
1875 pc = NULL;
b13d92d1 1876 continue;
22b4634c 1877 }
b13d92d1
VZ
1878
1879 // detect file format
223d09f6 1880 const wxChar *pEqualSign = wxStrchr(pc, wxT('='));
b13d92d1
VZ
1881 if ( pEqualSign == NULL ) {
1882 // brief format
1883 // ------------
1884
1885 // first field is mime type
223d09f6 1886 for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != wxT('\0'); pc++ ) {
b13d92d1
VZ
1887 strMimeType += *pc;
1888 }
1889
1890 // skip whitespace
50920146 1891 while ( wxIsspace(*pc) )
b13d92d1
VZ
1892 pc++;
1893
1894 // take all the rest of the string
1895 strExtensions = pc;
1896
1897 // no description...
1898 strDesc.Empty();
1899 }
1900 else {
1901 // expanded format
1902 // ---------------
1903
1904 // the string on the left of '=' is the field name
1905 wxString strLHS(pc, pEqualSign - pc);
1906
1907 // eat whitespace
50920146 1908 for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
b13d92d1
VZ
1909 ;
1910
50920146 1911 const wxChar *pEnd;
223d09f6 1912 if ( *pc == wxT('"') ) {
b13d92d1 1913 // the string is quoted and ends at the matching quote
223d09f6 1914 pEnd = wxStrchr(++pc, wxT('"'));
b13d92d1
VZ
1915 if ( pEnd == NULL ) {
1916 wxLogWarning(_("Mime.types file %s, line %d: unterminated "
1917 "quoted string."),
1918 strFileName.c_str(), nLine + 1);
1919 }
1920 }
1921 else {
22b4634c 1922 // unquoted string ends at the first space
50920146 1923 for ( pEnd = pc; !wxIsspace(*pEnd); pEnd++ )
b13d92d1
VZ
1924 ;
1925 }
1926
1927 // now we have the RHS (field value)
1928 wxString strRHS(pc, pEnd - pc);
1929
22b4634c 1930 // check what follows this entry
223d09f6 1931 if ( *pEnd == wxT('"') ) {
b13d92d1
VZ
1932 // skip this quote
1933 pEnd++;
1934 }
1935
50920146 1936 for ( pc = pEnd; wxIsspace(*pc); pc++ )
b13d92d1
VZ
1937 ;
1938
22b4634c
VZ
1939 // if there is something left, it may be either a '\\' to continue
1940 // the line or the next field of the same entry
223d09f6 1941 bool entryEnded = *pc == wxT('\0'),
22b4634c
VZ
1942 nextFieldOnSameLine = FALSE;
1943 if ( !entryEnded ) {
223d09f6 1944 nextFieldOnSameLine = ((*pc != wxT('\\')) || (pc[1] != wxT('\0')));
b13d92d1 1945 }
b13d92d1
VZ
1946
1947 // now see what we got
223d09f6 1948 if ( strLHS == wxT("type") ) {
b13d92d1
VZ
1949 strMimeType = strRHS;
1950 }
223d09f6 1951 else if ( strLHS == wxT("desc") ) {
b13d92d1
VZ
1952 strDesc = strRHS;
1953 }
223d09f6 1954 else if ( strLHS == wxT("exts") ) {
b13d92d1
VZ
1955 strExtensions = strRHS;
1956 }
1957 else {
1958 wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
1959 strFileName.c_str(), nLine + 1, strLHS.c_str());
1960 }
1961
1962 if ( !entryEnded ) {
22b4634c
VZ
1963 if ( !nextFieldOnSameLine )
1964 pc = NULL;
1965 //else: don't reset it
1966
1967 // as we don't reset strMimeType, the next field in this entry
b13d92d1 1968 // will be interpreted correctly.
22b4634c 1969
b13d92d1
VZ
1970 continue;
1971 }
1972 }
1973
a1d8eaf7
VZ
1974 // although it doesn't seem to be covered by RFCs, some programs
1975 // (notably Netscape) create their entries with several comma
1976 // separated extensions (RFC mention the spaces only)
223d09f6 1977 strExtensions.Replace(wxT(","), wxT(" "));
a1d8eaf7
VZ
1978
1979 // also deal with the leading dot
1b986aef
VZ
1980 if ( !strExtensions.IsEmpty() && strExtensions[0u] == wxT('.') )
1981 {
a1d8eaf7
VZ
1982 strExtensions.erase(0, 1);
1983 }
1984
8e124873 1985 AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
22b4634c
VZ
1986
1987 // finished with this line
1988 pc = NULL;
b13d92d1
VZ
1989 }
1990
1991 // check our data integriry
1992 wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
1993 m_aTypes.Count() == m_aExtensions.Count() &&
1994 m_aTypes.Count() == m_aDescriptions.Count() );
cc385968
VZ
1995
1996 return TRUE;
b13d92d1
VZ
1997}
1998
cc385968
VZ
1999bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
2000 bool fallback)
b13d92d1 2001{
223d09f6 2002 wxLogTrace(wxT("--- Parsing mailcap file '%s' ---"), strFileName.c_str());
b13d92d1
VZ
2003
2004 wxTextFile file(strFileName);
2005 if ( !file.Open() )
cc385968 2006 return FALSE;
b13d92d1 2007
cc385968
VZ
2008 // see the comments near the end of function for the reason we need these
2009 // variables (search for the next occurence of them)
2010 // indices of MIME types (in m_aTypes) we already found in this file
b13d92d1 2011 wxArrayInt aEntryIndices;
cc385968
VZ
2012 // aLastIndices[n] is the index of last element in
2013 // m_aEntries[aEntryIndices[n]] from this file
2014 wxArrayInt aLastIndices;
b13d92d1
VZ
2015
2016 size_t nLineCount = file.GetLineCount();
2017 for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
2018 // now we're at the start of the line
50920146 2019 const wxChar *pc = file[nLine].c_str();
b13d92d1
VZ
2020
2021 // skip whitespace
50920146 2022 while ( wxIsspace(*pc) )
b13d92d1
VZ
2023 pc++;
2024
2025 // comment or empty string?
223d09f6 2026 if ( *pc == wxT('#') || *pc == wxT('\0') )
b13d92d1
VZ
2027 continue;
2028
2029 // no, do parse
2030
2031 // what field are we currently in? The first 2 are fixed and there may
2032 // be an arbitrary number of other fields -- currently, we are not
2033 // interested in any of them, but we should parse them as well...
2034 enum
2035 {
2036 Field_Type,
2037 Field_OpenCmd,
2038 Field_Other
2039 } currentToken = Field_Type;
2040
2041 // the flags and field values on the current line
8fc613f1
RR
2042 bool needsterminal = FALSE,
2043 copiousoutput = FALSE;
b13d92d1
VZ
2044 wxString strType,
2045 strOpenCmd,
2046 strPrintCmd,
2047 strTest,
2048 strDesc,
2049 curField; // accumulator
2050 for ( bool cont = TRUE; cont; pc++ ) {
2051 switch ( *pc ) {
223d09f6 2052 case wxT('\\'):
b13d92d1
VZ
2053 // interpret the next character literally (notice that
2054 // backslash can be used for line continuation)
223d09f6 2055 if ( *++pc == wxT('\0') ) {
b13d92d1
VZ
2056 // fetch the next line.
2057
2058 // pc currently points to nowhere, but after the next
2059 // pc++ in the for line it will point to the beginning
2060 // of the next line in the file
2061 pc = file[++nLine].c_str() - 1;
2062 }
2063 else {
2064 // just a normal character
2065 curField += *pc;
2066 }
2067 break;
2068
223d09f6 2069 case wxT('\0'):
b13d92d1
VZ
2070 cont = FALSE; // end of line reached, exit the loop
2071
2072 // fall through
2073
223d09f6 2074 case wxT(';'):
b13d92d1
VZ
2075 // store this field and start looking for the next one
2076
2077 // trim whitespaces from both sides
2078 curField.Trim(TRUE).Trim(FALSE);
2079
2080 switch ( currentToken ) {
2081 case Field_Type:
2082 strType = curField;
223d09f6 2083 if ( strType.Find(wxT('/')) == wxNOT_FOUND ) {
b13d92d1 2084 // we interpret "type" as "type/*"
223d09f6 2085 strType += wxT("/*");
b13d92d1
VZ
2086 }
2087
2088 currentToken = Field_OpenCmd;
2089 break;
2090
2091 case Field_OpenCmd:
2092 strOpenCmd = curField;
2093
2094 currentToken = Field_Other;
2095 break;
2096
2097 case Field_Other:
2098 {
2099 // "good" mailcap entry?
2100 bool ok = TRUE;
2101
2102 // is this something of the form foo=bar?
223d09f6 2103 const wxChar *pEq = wxStrchr(curField, wxT('='));
b13d92d1 2104 if ( pEq != NULL ) {
223d09f6
KB
2105 wxString lhs = curField.BeforeFirst(wxT('=')),
2106 rhs = curField.AfterFirst(wxT('='));
b13d92d1
VZ
2107
2108 lhs.Trim(TRUE); // from right
2109 rhs.Trim(FALSE); // from left
2110
223d09f6 2111 if ( lhs == wxT("print") )
b13d92d1 2112 strPrintCmd = rhs;
223d09f6 2113 else if ( lhs == wxT("test") )
b13d92d1 2114 strTest = rhs;
223d09f6 2115 else if ( lhs == wxT("description") ) {
b13d92d1 2116 // it might be quoted
223d09f6
KB
2117 if ( rhs[0u] == wxT('"') &&
2118 rhs.Last() == wxT('"') ) {
b13d92d1
VZ
2119 strDesc = wxString(rhs.c_str() + 1,
2120 rhs.Len() - 2);
2121 }
2122 else {
2123 strDesc = rhs;
2124 }
2125 }
223d09f6
KB
2126 else if ( lhs == wxT("compose") ||
2127 lhs == wxT("composetyped") ||
2128 lhs == wxT("notes") ||
2129 lhs == wxT("edit") )
b13d92d1
VZ
2130 ; // ignore
2131 else
2132 ok = FALSE;
2133
2134 }
2135 else {
2136 // no, it's a simple flag
2137 // TODO support the flags:
2138 // 1. create an xterm for 'needsterminal'
2139 // 2. append "| $PAGER" for 'copiousoutput'
223d09f6 2140 if ( curField == wxT("needsterminal") )
b13d92d1 2141 needsterminal = TRUE;
223d09f6 2142 else if ( curField == wxT("copiousoutput") )
b13d92d1 2143 copiousoutput = TRUE;
223d09f6 2144 else if ( curField == wxT("textualnewlines") )
b13d92d1
VZ
2145 ; // ignore
2146 else
2147 ok = FALSE;
2148 }
2149
2150 if ( !ok )
2151 {
54acce90
VZ
2152 // we don't understand this field, but
2153 // Netscape stores info in it, so don't warn
2154 // about it
2155 if ( curField.Left(16u) != "x-mozilla-flags=" )
2156 {
2157 // don't flood the user with error
2158 // messages if we don't understand
2159 // something in his mailcap, but give
2160 // them in debug mode because this might
2161 // be useful for the programmer
2162 wxLogDebug
2163 (
2164 wxT("Mailcap file %s, line %d: "
2165 "unknown field '%s' for the "
2166 "MIME type '%s' ignored."),
2167 strFileName.c_str(),
2168 nLine + 1,
2169 curField.c_str(),
2170 strType.c_str()
2171 );
2172 }
b13d92d1
VZ
2173 }
2174 }
2175
2176 // it already has this value
2177 //currentToken = Field_Other;
2178 break;
2179
2180 default:
223d09f6 2181 wxFAIL_MSG(wxT("unknown field type in mailcap"));
b13d92d1
VZ
2182 }
2183
2184 // next token starts immediately after ';'
2185 curField.Empty();
2186 break;
2187
2188 default:
2189 curField += *pc;
2190 }
2191 }
2192
2193 // check that we really read something reasonable
2194 if ( currentToken == Field_Type || currentToken == Field_OpenCmd ) {
2195 wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
2196 "ignored."),
2197 strFileName.c_str(), nLine + 1);
2198 }
2199 else {
2200 MailCapEntry *entry = new MailCapEntry(strOpenCmd,
2201 strPrintCmd,
2202 strTest);
2203
8e124873
VZ
2204 // NB: because of complications below (we must get entries priority
2205 // right), we can't use AddMailcapInfo() here, unfortunately.
b13d92d1
VZ
2206 strType.MakeLower();
2207 int nIndex = m_aTypes.Index(strType);
3c67202d 2208 if ( nIndex == wxNOT_FOUND ) {
b13d92d1
VZ
2209 // new file type
2210 m_aTypes.Add(strType);
2211
2212 m_aEntries.Add(entry);
223d09f6 2213 m_aExtensions.Add(wxT(""));
b13d92d1
VZ
2214 m_aDescriptions.Add(strDesc);
2215 }
2216 else {
cc385968
VZ
2217 // modify the existing entry: the entries in one and the same
2218 // file are read in top-to-bottom order, i.e. the entries read
2219 // first should be tried before the entries below. However,
2220 // the files read later should override the settings in the
2221 // files read before (except if fallback is TRUE), thus we
2222 // Insert() the new entry to the list if it has already
2223 // occured in _this_ file, but Prepend() it if it occured in
2224 // some of the previous ones and Append() to it in the
2225 // fallback case
2226
2227 if ( fallback ) {
2228 // 'fallback' parameter prevents the entries from this
2229 // file from overriding the other ones - always append
2230 MailCapEntry *entryOld = m_aEntries[nIndex];
2231 if ( entryOld )
2232 entry->Append(entryOld);
2233 else
2234 m_aEntries[nIndex] = entry;
b13d92d1
VZ
2235 }
2236 else {
cc385968
VZ
2237 int entryIndex = aEntryIndices.Index(nIndex);
2238 if ( entryIndex == wxNOT_FOUND ) {
2239 // first time in this file
2240 aEntryIndices.Add(nIndex);
2241 aLastIndices.Add(0);
2242
2243 entry->Prepend(m_aEntries[nIndex]);
2244 m_aEntries[nIndex] = entry;
2245 }
2246 else {
2247 // not the first time in _this_ file
2248 size_t nEntryIndex = (size_t)entryIndex;
2249 MailCapEntry *entryOld = m_aEntries[nIndex];
2250 if ( entryOld )
2251 entry->Insert(entryOld, aLastIndices[nEntryIndex]);
2252 else
2253 m_aEntries[nIndex] = entry;
2254
2255 // the indices were shifted by 1
2256 aLastIndices[nEntryIndex]++;
2257 }
b13d92d1
VZ
2258 }
2259
2260 if ( !strDesc.IsEmpty() ) {
cc385968 2261 // replace the old one - what else can we do??
b13d92d1
VZ
2262 m_aDescriptions[nIndex] = strDesc;
2263 }
2264 }
2265 }
2266
2267 // check our data integriry
2268 wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
2269 m_aTypes.Count() == m_aExtensions.Count() &&
2270 m_aTypes.Count() == m_aDescriptions.Count() );
2271 }
cc385968
VZ
2272
2273 return TRUE;
b13d92d1
VZ
2274}
2275
696e1ea0 2276size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
1b986aef 2277{
54acce90
VZ
2278 mimetypes.Empty();
2279
2280 wxString type;
2281 size_t count = m_aTypes.GetCount();
2282 for ( size_t n = 0; n < count; n++ )
2283 {
2284 // don't return template types from here (i.e. anything containg '*')
2285 type = m_aTypes[n];
2286 if ( type.Find(_T('*')) == wxNOT_FOUND )
2287 {
2288 mimetypes.Add(type);
2289 }
2290 }
1b986aef 2291
54acce90 2292 return mimetypes.GetCount();
1b986aef
VZ
2293}
2294
8e124873 2295#endif
ce4169a4
RR
2296 // OS type
2297
8e124873 2298#endif
ce4169a4 2299 // wxUSE_FILE && wxUSE_TEXTFILE
b13d92d1 2300
3d05544e
JS
2301#endif
2302 // __WIN16__