]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/strconv.cpp
cleanup in toplevel activation, and switching to native carbon event notification...
[wxWidgets.git] / src / common / strconv.cpp
index d2c107ecbea724d0da1bb246074ffdf89550e1bc..4b2a491e13b75fa3bcbf283648d1b7143e74857d 100644 (file)
@@ -40,6 +40,7 @@
 
 #ifdef __WXMSW__
     #include "wx/msw/private.h"
+    #include "wx/msw/missing.h"
 #endif
 
 #ifndef __WXWINCE__
 #include "wx/encconv.h"
 #include "wx/fontmap.h"
 
+#ifdef __WXMAC__
+#include <ATSUnicode.h>
+#include <TextCommon.h>
+#include <TextEncodingConverter.h>
+
+#include  "wx/mac/private.h"  // includes mac headers
+#endif
 // ----------------------------------------------------------------------------
 // macros
 // ----------------------------------------------------------------------------
@@ -111,7 +119,8 @@ static size_t encode_utf16(wxUint32 input, wxUint16 *output)
 {
     if (input<=0xffff)
     {
-        if (output) *output++ = (wxUint16) input;
+        if (output)
+            *output = (wxUint16) input;
         return 1;
     }
     else if (input>=0x110000)
@@ -123,7 +132,7 @@ static size_t encode_utf16(wxUint32 input, wxUint16 *output)
         if (output)
         {
             *output++ = (wxUint16) ((input >> 10)+0xd7c0);
-            *output++ = (wxUint16) ((input&0x3ff)+0xdc00);
+            *output = (wxUint16) ((input&0x3ff)+0xdc00);
         }
         return 2;
     }
@@ -1143,33 +1152,263 @@ public:
         return len ? len - 1 : (size_t)-1;
     }
 
-    size_t WC2MB(char *buf, const wchar_t *psz, size_t n) const
+    size_t WC2MB(char *buf, const wchar_t *pwz, size_t n) const
     {
+        /*
+            we have a problem here: by default, WideCharToMultiByte() may
+            replace characters unrepresentable in the target code page with bad
+            quality approximations such as turning "1/2" symbol (U+00BD) into
+            "1" for the code pages which don't have it and we, obviously, want
+            to avoid this at any price
+        
+            the trouble is that this function does it _silently_, i.e. it won't
+            even tell us whether it did or not... Win98/2000 and higher provide
+            WC_NO_BEST_FIT_CHARS but it doesn't work for the older systems and
+            we have to resort to a round trip, i.e. check that converting back
+            results in the same string -- this is, of course, expensive but
+            otherwise we simply can't be sure to not garble the data.
+         */
+
+        // determine if we can rely on WC_NO_BEST_FIT_CHARS: according to MSDN
+        // it doesn't work with CJK encodings (which we test for rather roughly
+        // here...) nor with UTF-7/8 nor, of course, with Windows versions not
+        // supporting it
+        BOOL usedDef wxDUMMY_INITIALIZE(false),
+             *pUsedDef;
+        int flags;
+        if ( CanUseNoBestFit() && m_CodePage < 50000 )
+        {
+            // it's our lucky day
+            flags = WC_NO_BEST_FIT_CHARS;
+            pUsedDef = &usedDef;
+        }
+        else // old system or unsupported encoding
+        {
+            flags = 0;
+            pUsedDef = NULL;
+        }
+
         const size_t len = ::WideCharToMultiByte
                              (
                                 m_CodePage,     // code page
-                                0,              // flags (none)
-                                psz,            // input string
+                                flags,          // either none or no best fit
+                                pwz,            // input string
                                 -1,             // it is (wide) NUL-terminated
                                 buf,            // output buffer
                                 buf ? n : 0,    // and its size
                                 NULL,           // default "replacement" char
-                                NULL            // [out] was it used?
+                                pUsedDef        // [out] was it used?
                              );
 
+        if ( !len )
+        {
+            // function totally failed
+            return (size_t)-1;
+        }
+
+        // if we were really converting, check if we succeeded
+        if ( buf )
+        {
+            if ( flags )
+            {
+                // check if the conversion failed, i.e. if any replacements
+                // were done
+                if ( usedDef )
+                    return (size_t)-1;
+            }
+            else // we must resort to double tripping...
+            {
+                wxWCharBuffer wcBuf(n);
+                if ( MB2WC(wcBuf.data(), buf, n) == (size_t)-1 ||
+                        wcscmp(wcBuf, pwz) != 0 )
+                {
+                    // we didn't obtain the same thing we started from, hence
+                    // the conversion was lossy and we consider that it failed
+                    return (size_t)-1;
+                }
+            }
+        }
+
         // see the comment above for the reason of "len - 1"
-        return len ? len - 1 : (size_t)-1;
+        return len 1;
     }
 
-    bool IsOk() const
-        { return m_CodePage != -1; }
+    bool IsOk() const { return m_CodePage != -1; }
+
+private:
+    static bool CanUseNoBestFit()
+    {
+        static int s_isWin98Or2k = -1;
+
+        if ( s_isWin98Or2k == -1 )
+        {
+            int verMaj, verMin;
+            switch ( wxGetOsVersion(&verMaj, &verMin) )
+            {
+                case wxWIN95:
+                    s_isWin98Or2k = verMaj >= 4 && verMin >= 10;
+                    break;
+
+                case wxWINDOWS_NT:
+                    s_isWin98Or2k = verMaj >= 5;
+                    break;
+
+                default:
+                    // unknown, be conseravtive by default
+                    s_isWin98Or2k = 0;
+            }
+
+            wxASSERT_MSG( s_isWin98Or2k != -1, _T("should be set above") );
+        }
+
+        return s_isWin98Or2k == 1;
+    }
 
-public:
     long m_CodePage;
 };
 
 #endif // wxHAVE_WIN32_MB2WC
 
+// ============================================================================
+// Mac conversion classes
+// ============================================================================
+
+#if defined(__WXMAC__) && defined(TARGET_CARBON)
+
+class wxMBConv_mac : public wxMBConv
+{
+public:
+    wxMBConv_mac()
+    {
+        Init(CFStringGetSystemEncoding()) ;
+    }
+
+    wxMBConv_mac(const wxChar* name)
+    {
+       Init( wxMacGetSystemEncFromFontEnc(wxFontMapper::Get()->CharsetToEncoding(name, FALSE) ) ) ;
+    }
+
+    wxMBConv_mac(wxFontEncoding encoding)
+    {
+       Init( wxMacGetSystemEncFromFontEnc(encoding) );
+    }
+    
+       ~wxMBConv_mac()
+       {
+           OSStatus status = noErr ;
+           status = TECDisposeConverter(m_MB2WC_converter);
+           status = TECDisposeConverter(m_WC2MB_converter);            
+       }
+       
+    
+       void Init( TextEncodingBase encoding)
+       {
+           OSStatus status = noErr ;
+               m_char_encoding = encoding ;
+               m_unicode_encoding = CreateTextEncoding(kTextEncodingUnicodeDefault,0,kUnicode16BitFormat) ;
+
+           status = TECCreateConverter(&m_MB2WC_converter,
+                                       m_char_encoding,
+                                       m_unicode_encoding);
+           status = TECCreateConverter(&m_WC2MB_converter,
+                                       m_unicode_encoding,
+                                       m_char_encoding);
+       }
+       
+    size_t MB2WC(wchar_t *buf, const char *psz, size_t n) const
+    {
+           OSStatus status = noErr ;
+           ByteCount byteOutLen ;
+           ByteCount byteInLen = strlen(psz) ;
+               wchar_t *tbuf = NULL ;
+               UniChar* ubuf = NULL ;
+               size_t res = 0 ;
+               
+               if (buf == NULL)
+               {
+                       n = byteInLen ;
+                       tbuf = (wchar_t*) malloc( n * SIZEOF_WCHAR_T) ;
+               }
+           ByteCount byteBufferLen = n * sizeof( UniChar ) ; 
+#if SIZEOF_WCHAR_T == 4
+               ubuf = (UniChar*) malloc( byteBufferLen + 2 ) ;
+#else
+               ubuf = (UniChar*) (buf ? buf : tbuf) ;
+#endif
+           status = TECConvertText(m_MB2WC_converter, (ConstTextPtr) psz , byteInLen, &byteInLen,
+             (TextPtr) ubuf , byteBufferLen, &byteOutLen);
+#if SIZEOF_WCHAR_T == 4
+        // we have to terminate here, because n might be larger for the trailing zero, and if UniChar
+        // is not properly terminated we get random characters at the end
+        ubuf[byteOutLen / sizeof( UniChar ) ] = 0 ;
+               wxMBConvUTF16BE converter ;
+               res = converter.MB2WC( (buf ? buf : tbuf) , (const char*)ubuf , n ) ;
+               free( ubuf ) ;
+#else
+               res = byteOutLen / sizeof( UniChar ) ;
+#endif
+               if ( buf == NULL )
+                       free(tbuf) ;
+
+        if ( buf  && res < n)
+            buf[res] = 0;
+
+               return res ;
+    }
+
+    size_t WC2MB(char *buf, const wchar_t *psz, size_t n) const
+    {          
+           OSStatus status = noErr ;
+           ByteCount byteOutLen ;
+           ByteCount byteInLen = wxWcslen(psz) * SIZEOF_WCHAR_T ;
+
+               char *tbuf = NULL ;
+               
+               if (buf == NULL)
+               {
+                       // worst case
+                       n = byteInLen * 2 ;
+                       tbuf = (char*) malloc( n ) ;
+               }
+
+           ByteCount byteBufferLen = n ;
+               UniChar* ubuf = NULL ;
+#if SIZEOF_WCHAR_T == 4
+               wxMBConvUTF16BE converter ;
+               size_t unicharlen = converter.WC2MB( NULL , psz , 0 ) ;
+               byteInLen = unicharlen ;
+               ubuf = (UniChar*) malloc( byteInLen + 2 ) ;
+               converter.WC2MB( (char*) ubuf , psz, unicharlen + 2 ) ;
+#else
+               ubuf = (UniChar*) psz ;
+#endif
+           status = TECConvertText(m_WC2MB_converter, (ConstTextPtr) ubuf , byteInLen, &byteInLen,
+              (TextPtr) (buf ? buf : tbuf) , byteBufferLen, &byteOutLen);
+#if SIZEOF_WCHAR_T == 4
+               free( ubuf ) ;
+#endif
+               if ( buf == NULL )
+                       free(tbuf) ;
+
+               size_t res = byteOutLen ;
+        if ( buf  && res < n)
+            buf[res] = 0;
+
+               return res ;
+    }
+
+    bool IsOk() const
+        { return m_MB2WC_converter !=  NULL && m_WC2MB_converter != NULL  ; }
+
+private:
+       TECObjectRef m_MB2WC_converter ;
+       TECObjectRef m_WC2MB_converter ;
+       
+       TextEncodingBase m_char_encoding ;
+       TextEncodingBase m_unicode_encoding ;
+};
+
+#endif // defined(__WXMAC__) && defined(TARGET_CARBON)
 
 // ============================================================================
 // wxEncodingConverter based conversion classes
@@ -1316,28 +1555,12 @@ void wxCSConv::SetName(const wxChar *charset)
     }
 }
 
-static inline bool DoesntNeedConv(wxFontEncoding enc)
-{
-    return enc == wxFONTENCODING_DEFAULT ||
-            enc == wxFONTENCODING_SYSTEM ||
-             enc == wxFONTENCODING_ISO8859_1;
-}
-
 wxMBConv *wxCSConv::DoCreate() const
 {
-#if wxUSE_FONTMAP
-    wxFontMapper * const fontMapper = wxFontMapper::Get();
-
-    wxFontEncoding encFromName = m_name ? fontMapper->CharsetToEncoding(m_name)
-                                        : wxFONTENCODING_SYSTEM;
-#endif // wxUSE_FONTMAP
-
-    // check for the special case of ASCII charset
-    if ( (!m_name && DoesntNeedConv(m_encoding))
-#if wxUSE_FONTMAP
-            || (m_name && DoesntNeedConv(encFromName))
-#endif // wxUSE_FONTMAP
-       )
+    // check for the special case of ASCII or ISO8859-1 charset: as we have
+    // special knowledge of it anyhow, we don't need to create a special
+    // conversion object
+    if ( m_encoding == wxFONTENCODING_ISO8859_1 )
     {
         // don't convert at all
         return NULL;
@@ -1353,9 +1576,18 @@ wxMBConv *wxCSConv::DoCreate() const
 
     // step (1)
 #ifdef HAVE_ICONV
+#if !wxUSE_FONTMAP
     if ( m_name )
+#endif // !wxUSE_FONTMAP
     {
-        wxMBConv_iconv *conv = new wxMBConv_iconv(m_name);
+        wxString name(m_name);
+
+#if wxUSE_FONTMAP
+        if ( name.empty() )
+            name = wxFontMapper::Get()->GetEncodingName(m_encoding);
+#endif // wxUSE_FONTMAP
+
+        wxMBConv_iconv *conv = new wxMBConv_iconv(name);
         if ( conv->IsOk() )
             return conv;
 
@@ -1373,12 +1605,30 @@ wxMBConv *wxCSConv::DoCreate() const
         delete conv;
     }
 #endif // wxHAVE_WIN32_MB2WC
-
+#if defined(__WXMAC__) 
+    {
+       if ( m_name || ( m_encoding < wxFONTENCODING_UTF16BE ) )
+       {
+                       
+               wxMBConv_mac *conv = m_name ? new wxMBConv_mac(m_name)
+                                           : new wxMBConv_mac(m_encoding);
+               if ( conv->IsOk() )
+                   return conv;
+
+               delete conv;
+       }
+    }
+#endif
     // step (2)
     wxFontEncoding enc = m_encoding;
 #if wxUSE_FONTMAP
-    if ( enc == wxFONTENCODING_SYSTEM )
-        enc = encFromName;
+    if ( enc == wxFONTENCODING_SYSTEM && m_name )
+    {
+        // use "false" to suppress interactive dialogs -- we can be called from
+        // anywhere and popping up a dialog from here is the last thing we want to
+        // do
+        enc = wxFontMapper::Get()->CharsetToEncoding(m_name, false);
+    }
 #endif // wxUSE_FONTMAP
 
     switch ( enc )
@@ -1418,8 +1668,19 @@ wxMBConv *wxCSConv::DoCreate() const
     }
 #endif // wxUSE_FONTMAP
 
-    wxLogError(_("Cannot convert from the charset '%s'!"),
-               m_name ? m_name
+    // NB: This is a hack to prevent deadlock. What could otherwise happen
+    //     in Unicode build: wxConvLocal creation ends up being here
+    //     because of some failure and logs the error. But wxLog will try to
+    //     attach timestamp, for which it will need wxConvLocal (to convert
+    //     time to char* and then wchar_t*), but that fails, tries to log
+    //     error, but wxLog has a (already locked) critical section that
+    //     guards static buffer.
+    static bool alreadyLoggingError = false;
+    if (!alreadyLoggingError)
+    {
+        alreadyLoggingError = true;
+        wxLogError(_("Cannot convert from the charset '%s'!"),
+                   m_name ? m_name
                       :
 #if wxUSE_FONTMAP
                          wxFontMapper::GetEncodingDescription(m_encoding).c_str()
@@ -1427,6 +1688,8 @@ wxMBConv *wxCSConv::DoCreate() const
                          wxString::Format(_("encoding %s"), m_encoding).c_str()
 #endif // wxUSE_FONTMAP/!wxUSE_FONTMAP
               );
+        alreadyLoggingError = false;
+    }
 
     return NULL;
 }
@@ -1442,7 +1705,7 @@ void wxCSConv::CreateConvIfNeeded() const
         // encoding for this system
         if ( !m_name && m_encoding == wxFONTENCODING_SYSTEM )
         {
-            self->m_encoding = wxLocale::GetSystemEncoding();
+            self->m_name = wxStrdup(wxLocale::GetSystemEncodingName());
         }
 #endif // wxUSE_INTL
 
@@ -1482,7 +1745,19 @@ size_t wxCSConv::WC2MB(char *buf, const wchar_t *psz, size_t n) const
     if (buf)
     {
         for (size_t c = 0; c <= len; c++)
-            buf[c] = (psz[c] > 0xff) ? '?' : psz[c];
+        {
+            if (psz[c] > 0xFF)
+                return (size_t)-1;
+            buf[c] = psz[c];
+        }
+    }
+    else
+    {
+        for (size_t c = 0; c <= len; c++)
+        {
+            if (psz[c] > 0xFF)
+                return (size_t)-1;
+        }
     }
 
     return len;