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