]>
git.saurik.com Git - wxWidgets.git/blob - src/msw/mimetype.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/mimetype.cpp
3 // Purpose: classes and functions to manage MIME types
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "mimetype.h"
16 // for compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
25 // Doesn't compile in WIN16 mode
29 #include "wx/string.h"
32 #include "wx/msgdlg.h"
38 #include "wx/iconloc.h"
40 #include "wx/dynarray.h"
41 #include "wx/confbase.h"
44 #include "wx/msw/registry.h"
45 #include "wx/msw/private.h"
48 #include "wx/msw/mimetype.h"
50 // other standard headers
53 // in case we're compiling in non-GUI mode
54 class WXDLLEXPORT wxIcon
;
56 // These classes use Windows registry to retrieve the required information.
58 // Keys used (not all of them are documented, so it might actually stop working
59 // in future versions of Windows...):
60 // 1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME
61 // types, each key has a string value "Extension" which gives (dot preceded)
62 // extension for the files of this MIME type.
64 // 2. "HKCR\.ext" contains
65 // a) unnamed value containing the "filetype"
66 // b) value "Content Type" containing the MIME type
68 // 3. "HKCR\filetype" contains
69 // a) unnamed value containing the description
70 // b) subkey "DefaultIcon" with single unnamed value giving the icon index in
72 // c) shell\open\command and shell\open\print subkeys containing the commands
73 // to open/print the file (the positional parameters are introduced by %1,
74 // %2, ... in these strings, we change them to %s ourselves)
76 // although I don't know of any official documentation which mentions this
77 // location, uses it, so it isn't likely to change
78 static const wxChar
*MIME_DATABASE_KEY
= wxT("MIME\\Database\\Content Type\\");
80 // this function replaces Microsoft %1 with Unix-like %s
81 static bool CanonicalizeParams(wxString
& command
)
83 // transform it from '%1' to '%s' style format string (now also test for %L
84 // as apparently MS started using it as well for the same purpose)
86 // NB: we don't make any attempt to verify that the string is valid, i.e.
87 // doesn't contain %2, or second %1 or .... But we do make sure that we
88 // return a string with _exactly_ one '%s'!
89 bool foundFilename
= false;
90 size_t len
= command
.length();
91 for ( size_t n
= 0; (n
< len
) && !foundFilename
; n
++ )
93 if ( command
[n
] == wxT('%') &&
95 (command
[n
+ 1] == wxT('1') || command
[n
+ 1] == wxT('L')) )
97 // replace it with '%s'
98 command
[n
+ 1] = wxT('s');
100 foundFilename
= true;
104 return foundFilename
;
107 void wxFileTypeImpl::Init(const wxString
& strFileType
, const wxString
& ext
)
109 // VZ: does it? (FIXME)
110 wxCHECK_RET( !ext
.IsEmpty(), _T("needs an extension") );
112 if ( ext
[0u] != wxT('.') ) {
117 m_strFileType
= strFileType
;
118 if ( !strFileType
) {
119 m_strFileType
= m_ext
.AfterFirst('.') + _T("_auto_file");
123 wxString
wxFileTypeImpl::GetVerbPath(const wxString
& verb
) const
126 path
<< m_strFileType
<< _T("\\shell\\") << verb
<< _T("\\command");
130 size_t wxFileTypeImpl::GetAllCommands(wxArrayString
*verbs
,
131 wxArrayString
*commands
,
132 const wxFileType::MessageParameters
& params
) const
134 wxCHECK_MSG( !m_ext
.IsEmpty(), 0, _T("GetAllCommands() needs an extension") );
136 if ( m_strFileType
.IsEmpty() )
138 // get it from the registry
139 wxFileTypeImpl
*self
= wxConstCast(this, wxFileTypeImpl
);
140 wxRegKey
rkey(wxRegKey::HKCR
, m_ext
);
141 if ( !rkey
.Exists() || !rkey
.QueryValue(wxEmptyString
, self
->m_strFileType
) )
143 wxLogDebug(_T("Can't get the filetype for extension '%s'."),
150 // enum all subkeys of HKCR\filetype\shell
152 wxRegKey
rkey(wxRegKey::HKCR
, m_strFileType
+ _T("\\shell"));
155 bool ok
= rkey
.GetFirstKey(verb
, dummy
);
158 wxString command
= wxFileType::ExpandCommand(GetCommand(verb
), params
);
160 // we want the open bverb to eb always the first
162 if ( verb
.CmpNoCase(_T("open")) == 0 )
165 verbs
->Insert(verb
, 0);
167 commands
->Insert(command
, 0);
169 else // anything else than "open"
174 commands
->Add(command
);
179 ok
= rkey
.GetNextKey(verb
, dummy
);
185 // ----------------------------------------------------------------------------
186 // modify the registry database
187 // ----------------------------------------------------------------------------
189 bool wxFileTypeImpl::EnsureExtKeyExists()
191 wxRegKey
rkey(wxRegKey::HKCR
, m_ext
);
192 if ( !rkey
.Exists() )
194 if ( !rkey
.Create() || !rkey
.SetValue(wxEmptyString
, m_strFileType
) )
196 wxLogError(_("Failed to create registry entry for '%s' files."),
205 // ----------------------------------------------------------------------------
206 // get the command to use
207 // ----------------------------------------------------------------------------
209 wxString
wxFileTypeImpl::GetCommand(const wxChar
*verb
) const
211 // suppress possible error messages
215 if ( wxRegKey(wxRegKey::HKCR
, m_ext
+ _T("\\shell")).Exists() )
217 if ( wxRegKey(wxRegKey::HKCR
, m_strFileType
+ _T("\\shell")).Exists() )
218 strKey
= m_strFileType
;
223 return wxEmptyString
;
226 strKey
<< wxT("\\shell\\") << verb
;
227 wxRegKey
key(wxRegKey::HKCR
, strKey
+ _T("\\command"));
230 // it's the default value of the key
231 if ( key
.QueryValue(wxEmptyString
, command
) ) {
232 bool foundFilename
= CanonicalizeParams(command
);
235 // look whether we must issue some DDE requests to the application
236 // (and not just launch it)
237 strKey
+= _T("\\DDEExec");
238 wxRegKey
keyDDE(wxRegKey::HKCR
, strKey
);
239 if ( keyDDE
.Open() ) {
240 wxString ddeCommand
, ddeServer
, ddeTopic
;
241 keyDDE
.QueryValue(wxEmptyString
, ddeCommand
);
242 ddeCommand
.Replace(_T("%1"), _T("%s"));
244 wxRegKey(wxRegKey::HKCR
, strKey
+ _T("\\Application")).
245 QueryValue(wxEmptyString
, ddeServer
);
246 wxRegKey(wxRegKey::HKCR
, strKey
+ _T("\\Topic")).
247 QueryValue(wxEmptyString
, ddeTopic
);
249 if (ddeTopic
.IsEmpty())
250 ddeTopic
= wxT("System");
252 // HACK: we use a special feature of wxExecute which exists
253 // just because we need it here: it will establish DDE
254 // conversation with the program it just launched
255 command
.Prepend(_T("WX_DDE#"));
256 command
<< _T('#') << ddeServer
257 << _T('#') << ddeTopic
258 << _T('#') << ddeCommand
;
262 if ( !foundFilename
)
264 // we didn't find any '%1' - the application doesn't know which
265 // file to open (note that we only do it if there is no DDEExec
268 // HACK: append the filename at the end, hope that it will do
269 command
<< wxT(" %s");
273 //else: no such file type or no value, will return empty string
279 wxFileTypeImpl::GetOpenCommand(wxString
*openCmd
,
280 const wxFileType::MessageParameters
& params
)
283 wxString cmd
= GetCommand(wxT("open"));
285 *openCmd
= wxFileType::ExpandCommand(cmd
, params
);
287 return !openCmd
->IsEmpty();
291 wxFileTypeImpl::GetPrintCommand(wxString
*printCmd
,
292 const wxFileType::MessageParameters
& params
)
295 wxString cmd
= GetCommand(wxT("print"));
297 *printCmd
= wxFileType::ExpandCommand(cmd
, params
);
299 return !printCmd
->IsEmpty();
302 // ----------------------------------------------------------------------------
303 // getting other stuff
304 // ----------------------------------------------------------------------------
306 // TODO this function is half implemented
307 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
309 if ( m_ext
.IsEmpty() ) {
310 // the only way to get the list of extensions from the file type is to
311 // scan through all extensions in the registry - too slow...
316 extensions
.Add(m_ext
);
318 // it's a lie too, we don't return _all_ extensions...
323 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
325 // suppress possible error messages
327 wxRegKey
key(wxRegKey::HKCR
, m_ext
);
329 return key
.Open() && key
.QueryValue(wxT("Content Type"), *mimeType
);
332 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const
336 if ( !GetMimeType(&s
) )
347 bool wxFileTypeImpl::GetIcon(wxIconLocation
*iconLoc
) const
350 strIconKey
<< m_strFileType
<< wxT("\\DefaultIcon");
352 // suppress possible error messages
354 wxRegKey
key(wxRegKey::HKCR
, strIconKey
);
358 // it's the default value of the key
359 if ( key
.QueryValue(wxEmptyString
, strIcon
) ) {
360 // the format is the following: <full path to file>, <icon index>
361 // NB: icon index may be negative as well as positive and the full
362 // path may contain the environment variables inside '%'
363 wxString strFullPath
= strIcon
.BeforeLast(wxT(',')),
364 strIndex
= strIcon
.AfterLast(wxT(','));
366 // index may be omitted, in which case BeforeLast(',') is empty and
367 // AfterLast(',') is the whole string
368 if ( strFullPath
.IsEmpty() ) {
369 strFullPath
= strIndex
;
375 iconLoc
->SetFileName(wxExpandEnvVars(strFullPath
));
377 iconLoc
->SetIndex(wxAtoi(strIndex
));
384 // no such file type or no value or incorrect icon entry
388 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
390 // suppress possible error messages
392 wxRegKey
key(wxRegKey::HKCR
, m_strFileType
);
395 // it's the default value of the key
396 if ( key
.QueryValue(wxEmptyString
, *desc
) ) {
406 wxMimeTypesManagerImpl::CreateFileType(const wxString
& filetype
, const wxString
& ext
)
408 wxFileType
*fileType
= new wxFileType
;
409 fileType
->m_impl
->Init(filetype
, ext
);
413 // extension -> file type
415 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
)
417 // add the leading point if necessary
419 if ( ext
[0u] != wxT('.') ) {
424 // suppress possible error messages
427 bool knownExtension
= FALSE
;
429 wxString strFileType
;
430 wxRegKey
key(wxRegKey::HKCR
, str
);
432 // it's the default value of the key
433 if ( key
.QueryValue(wxEmptyString
, strFileType
) ) {
434 // create the new wxFileType object
435 return CreateFileType(strFileType
, ext
);
438 // this extension doesn't have a filetype, but it's known to the
439 // system and may be has some other useful keys (open command or
440 // content-type), so still return a file type object for it
441 knownExtension
= TRUE
;
445 if ( !knownExtension
)
451 return CreateFileType(wxEmptyString
, ext
);
456 wxMimeTypesManagerImpl::GetOrAllocateFileTypeFromExtension(const wxString& ext)
458 wxFileType *fileType = GetFileTypeFromExtension(ext);
461 fileType = CreateFileType(wxEmptyString, ext);
468 // MIME type -> extension -> file type
470 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
472 wxString strKey
= MIME_DATABASE_KEY
;
475 // suppress possible error messages
479 wxRegKey
key(wxRegKey::HKCR
, strKey
);
481 if ( key
.QueryValue(wxT("Extension"), ext
) ) {
482 return GetFileTypeFromExtension(ext
);
490 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
)
492 // enumerate all keys under MIME_DATABASE_KEY
493 wxRegKey
key(wxRegKey::HKCR
, MIME_DATABASE_KEY
);
497 bool cont
= key
.GetFirstKey(type
, cookie
);
502 cont
= key
.GetNextKey(type
, cookie
);
505 return mimetypes
.GetCount();
508 // ----------------------------------------------------------------------------
509 // create a new association
510 // ----------------------------------------------------------------------------
512 wxFileType
*wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
)
514 wxCHECK_MSG( !ftInfo
.GetExtensions().IsEmpty(), NULL
,
515 _T("Associate() needs extension") );
522 wxString ext
= ftInfo
.GetExtensions()[iExtCount
];
524 wxCHECK_MSG( !ext
.empty(), NULL
,
525 _T("Associate() needs non empty extension") );
527 if ( ext
[0u] != _T('.') )
528 extWithDot
= _T('.');
531 // start by setting the HKCR\\.ext entries
532 // default is filetype; content type is mimetype
533 const wxString
& filetypeOrig
= ftInfo
.GetShortDesc();
535 wxRegKey
key(wxRegKey::HKCR
, extWithDot
);
538 // create the mapping from the extension to the filetype
543 if ( filetypeOrig
.empty() )
545 // make it up from the extension
546 filetype
<< extWithDot
.c_str() + 1 << _T("_file");
550 // just use the provided one
551 filetype
= filetypeOrig
;
554 key
.SetValue(wxEmptyString
, filetype
);
559 // key already exists, maybe we want to change it ??
560 if (!filetypeOrig
.empty())
562 filetype
= filetypeOrig
;
563 key
.SetValue(wxEmptyString
, filetype
);
567 key
.QueryValue(wxEmptyString
, filetype
);
570 // now set a mimetypeif we have it, but ignore it if none
571 const wxString
& mimetype
= ftInfo
.GetMimeType();
572 if ( !mimetype
.empty() )
575 ok
= key
.SetValue(_T("Content Type"), mimetype
);
579 // create the MIME key
580 wxString strKey
= MIME_DATABASE_KEY
;
582 wxRegKey
keyMIME(wxRegKey::HKCR
, strKey
);
583 ok
= keyMIME
.Create();
587 // and provide a back link to the extension
588 keyMIME
.SetValue(_T("Extension"), extWithDot
);
594 // now make other extensions have the same filetype
596 for (iExtCount
=1; iExtCount
< ftInfo
.GetExtensionsCount(); iExtCount
++ )
598 ext
= ftInfo
.GetExtensions()[iExtCount
];
599 if ( ext
[0u] != _T('.') )
600 extWithDot
= _T('.');
603 wxRegKey
key(wxRegKey::HKCR
, extWithDot
);
604 if ( !key
.Exists() ) key
.Create();
605 key
.SetValue(wxEmptyString
, filetype
);
607 // now set any mimetypes we may have, but ignore it if none
608 const wxString
& mimetype
= ftInfo
.GetMimeType();
609 if ( !mimetype
.empty() )
612 ok
= key
.SetValue(_T("Content Type"), mimetype
);
616 // create the MIME key
617 wxString strKey
= MIME_DATABASE_KEY
;
619 wxRegKey
keyMIME(wxRegKey::HKCR
, strKey
);
620 ok
= keyMIME
.Create();
624 // and provide a back link to the extension
625 keyMIME
.SetValue(_T("Extension"), extWithDot
);
631 } // end of for loop; all extensions now point to HKCR\.ext\Default
633 // create the filetype key itself (it will be empty for now, but
634 // SetCommand(), SetDefaultIcon() &c will use it later)
635 wxRegKey
keyFT(wxRegKey::HKCR
, filetype
);
638 wxFileType
*ft
= CreateFileType(filetype
, extWithDot
);
642 if (! ftInfo
.GetOpenCommand ().IsEmpty() ) ft
->SetCommand (ftInfo
.GetOpenCommand (), wxT("open" ) );
643 if (! ftInfo
.GetPrintCommand().IsEmpty() ) ft
->SetCommand (ftInfo
.GetPrintCommand(), wxT("print" ) );
644 // chris: I don't like the ->m_impl-> here FIX this ??
645 if (! ftInfo
.GetDescription ().IsEmpty() ) ft
->m_impl
->SetDescription (ftInfo
.GetDescription ()) ;
646 if (! ftInfo
.GetIconFile().IsEmpty() ) ft
->SetDefaultIcon (ftInfo
.GetIconFile(), ftInfo
.GetIconIndex() );
652 bool wxFileTypeImpl::SetCommand(const wxString
& cmd
,
653 const wxString
& verb
,
654 bool WXUNUSED(overwriteprompt
))
656 wxCHECK_MSG( !m_ext
.IsEmpty() && !verb
.IsEmpty(), FALSE
,
657 _T("SetCommand() needs an extension and a verb") );
659 if ( !EnsureExtKeyExists() )
662 wxRegKey
rkey(wxRegKey::HKCR
, GetVerbPath(verb
));
664 if ( rkey
.Exists() && overwriteprompt
)
668 rkey
.QueryValue(wxEmptyString
, old
);
672 _("Do you want to overwrite the command used to %s "
673 "files with extension \"%s\" ?\nCurrent value is \n%s, "
674 "\nNew value is \n%s %1"), // bug here FIX need %1 ??
679 _("Confirm registry update"),
680 wxYES_NO
| wxICON_QUESTION
690 // 1. translate '%s' to '%1' instead of always adding it
691 // 2. create DDEExec value if needed (undo GetCommand)
692 return rkey
.Create() && rkey
.SetValue(wxEmptyString
, cmd
+ _T(" \"%1\"") );
696 bool wxFileTypeImpl::SetMimeType(const wxString& mimeTypeOrig)
698 wxCHECK_MSG( !m_ext.IsEmpty(), FALSE, _T("SetMimeType() needs extension") );
700 if ( !EnsureExtKeyExists() )
703 // VZ: is this really useful? (FIXME)
707 // make up a default value for it
709 wxSplitPath(GetCommand(_T("open")), NULL, &cmd, NULL);
710 mimeType << _T("application/x-") << cmd;
714 mimeType = mimeTypeOrig;
717 wxRegKey rkey(wxRegKey::HKCR, m_ext);
718 return rkey.Create() && rkey.SetValue(_T("Content Type"), mimeType);
722 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& cmd
, int index
)
724 wxCHECK_MSG( !m_ext
.IsEmpty(), FALSE
, _T("SetDefaultIcon() needs extension") );
725 wxCHECK_MSG( !m_strFileType
.IsEmpty(), FALSE
, _T("File key not found") );
726 // the next line fails on a SMBshare, I think because it is case mangled
727 // wxCHECK_MSG( !wxFileExists(cmd), FALSE, _T("Icon file not found.") );
729 if ( !EnsureExtKeyExists() )
732 wxRegKey
rkey(wxRegKey::HKCR
, m_strFileType
+ _T("\\DefaultIcon"));
734 return rkey
.Create() &&
735 rkey
.SetValue(wxEmptyString
,
736 wxString::Format(_T("%s,%d"), cmd
.c_str(), index
));
739 bool wxFileTypeImpl::SetDescription (const wxString
& desc
)
741 wxCHECK_MSG( !m_strFileType
.IsEmpty(), FALSE
, _T("File key not found") );
742 wxCHECK_MSG( !desc
.IsEmpty(), FALSE
, _T("No file description supplied") );
744 if ( !EnsureExtKeyExists() )
747 wxRegKey
rkey(wxRegKey::HKCR
, m_strFileType
);
749 return rkey
.Create() &&
750 rkey
.SetValue(wxEmptyString
, desc
);
753 // ----------------------------------------------------------------------------
754 // remove file association
755 // ----------------------------------------------------------------------------
757 bool wxFileTypeImpl::Unassociate()
760 if ( !RemoveOpenCommand() )
762 if ( !RemoveDefaultIcon() )
764 if ( !RemoveMimeType() )
766 if ( !RemoveDescription() )
770 //this might hold other keys, eg some have CSLID keys
773 // delete the root key
774 wxRegKey key(wxRegKey::HKCR, m_ext);
776 result = key.DeleteSelf();
782 bool wxFileTypeImpl::RemoveOpenCommand()
784 return RemoveCommand(_T("open"));
787 bool wxFileTypeImpl::RemoveCommand(const wxString
& verb
)
789 wxCHECK_MSG( !m_ext
.IsEmpty() && !verb
.IsEmpty(), FALSE
,
790 _T("RemoveCommand() needs an extension and a verb") );
792 wxString sKey
= m_strFileType
;
793 wxRegKey
rkey(wxRegKey::HKCR
, GetVerbPath(verb
));
795 // if the key already doesn't exist, it's a success
796 return !rkey
.Exists() || rkey
.DeleteSelf();
799 bool wxFileTypeImpl::RemoveMimeType()
801 wxCHECK_MSG( !m_ext
.IsEmpty(), FALSE
, _T("RemoveMimeType() needs extension") );
803 wxRegKey
rkey(wxRegKey::HKCR
, m_ext
);
804 return !rkey
.Exists() || rkey
.DeleteSelf();
807 bool wxFileTypeImpl::RemoveDefaultIcon()
809 wxCHECK_MSG( !m_ext
.IsEmpty(), FALSE
,
810 _T("RemoveDefaultIcon() needs extension") );
812 wxRegKey
rkey (wxRegKey::HKCR
, m_strFileType
+ _T("\\DefaultIcon"));
813 return !rkey
.Exists() || rkey
.DeleteSelf();
816 bool wxFileTypeImpl::RemoveDescription()
818 wxCHECK_MSG( !m_ext
.IsEmpty(), FALSE
,
819 _T("RemoveDescription() needs extension") );
821 wxRegKey
rkey (wxRegKey::HKCR
, m_strFileType
);
822 return !rkey
.Exists() || rkey
.DeleteSelf();
828 #endif // wxUSE_MIMETYPE