KiCad PCB EDA Suite
Loading...
Searching...
No Matches
locale_io.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <locale_io.h>
21#include <wx/intl.h>
22#include <clocale>
23#include <mutex>
24
25// When reading/writing files, we need to switch to setlocale( LC_NUMERIC, "C" ).
26// Works fine to read/write files with floating point numbers.
27// We can call setlocale( LC_NUMERIC, "C" ) or wxLocale( "C", "C", "C", false )
28// wxWidgets discourage a direct call to setlocale
29// However, for us, calling wxLocale( "C", "C", "C", false ) has a unwanted effect:
30// The I18N translations are no longer active, because the English dictionary is selected.
31// To read files, this is not a major issues, but the result can differ
32// from using setlocale(xx, "C").
33// Previously, we used only setlocale( LC_NUMERIC, "C" )
34//
35// Known issues are
36// on MSW
37// using setlocale( LC_NUMERIC, "C" ) generates an alert message in debug mode,
38// and this message ("Decimal separator mismatch") must be disabled.
39// But calling wxLocale( "C", "C", "C", false ) works fine
40// On unix:
41// calling wxLocale( "C", "C", "C", false ) breaks env vars containing non ASCII7 chars.
42// these env vars return a empty string from wxGetEnv() in many cases, and if such a
43// var must be read after calling wxLocale( "C", "C", "C", false ), it looks like empty
44//
45// So use wxLocale on Windows and setlocale on unix
46
47// On Windows, when using setlocale, a wx alert is generated
48// in some cases (reading a bitmap for instance)
49// So we disable alerts during the time a file is read or written
50
51
52// set USE_WXLOCALE to 0 to use setlocale, 1 to use wxLocale:
53#if defined( _WIN32 )
54#define USE_WXLOCALE 1
55#else
56#define USE_WXLOCALE 0
57#endif
58
59#if !USE_WXLOCALE
60#if defined( _WIN32 ) && defined( DEBUG )
61#include <wx/appl.h> // for wxTheApp
62
63// a wxAssertHandler_t function to filter wxWidgets alert messages when reading/writing a file
64// when switching the locale to LC_NUMERIC, "C"
65// It is used in class LOCALE_IO to hide a useless (in KiCad) wxWidgets alert message
66void KiAssertFilter( const wxString &file, int line,
67 const wxString &func, const wxString &cond,
68 const wxString &msg)
69{
70 if( !msg.Contains( "Decimal separator mismatch" ) )
71 wxTheApp->OnAssertFailure( file.c_str(), line, func.c_str(), cond.c_str(), msg.c_str() );
72}
73#endif
74#endif
75
76// Allow for nesting of LOCALE_IO instantiations. SPICE library parsing (and other code) toggles
77// the locale from worker threads in parallel, so both the nesting count and the global locale
78// transitions it triggers must be serialized together under one mutex. An atomic count alone is
79// not enough, since setlocale() and the wxLocale new/delete it guards mutate process-global state
80// and the heap, which corrupts when two threads transition at the same time.
81static std::mutex locale_mutex;
82static unsigned int locale_count = 0;
83static wxLocale* locale_wxLocale = nullptr;
84static std::string locale_user_locale;
85
87{
88 std::lock_guard<std::mutex> lock( locale_mutex );
89
90 if( locale_count++ == 0 )
91 {
92#if USE_WXLOCALE
93 #define C_LANG "C"
94 locale_wxLocale = new wxLocale( C_LANG, C_LANG, C_LANG, false );
95#else
96 // Store the user locale name, to restore this locale later, in dtor
97 locale_user_locale = setlocale( LC_NUMERIC, nullptr );
98#if defined( _WIN32 ) && defined( DEBUG )
99 // Disable wxWidgets alerts
100 wxSetAssertHandler( KiAssertFilter );
101#endif
102 // Switch the locale to C locale, to read/write files with fp numbers
103 setlocale( LC_NUMERIC, "C" );
104#endif
105 }
106}
107
108
110{
111 std::lock_guard<std::mutex> lock( locale_mutex );
112
113 if( --locale_count == 0 )
114 {
115 // revert to the user locale
116#if USE_WXLOCALE
117 delete locale_wxLocale; // Deleting the wxLocale restored the previous locale
118 locale_wxLocale = nullptr;
119#else
120 setlocale( LC_NUMERIC, locale_user_locale.c_str() );
121#if defined( _WIN32 ) && defined( DEBUG )
122 // Enable wxWidgets alerts
123 wxSetDefaultAssertHandler();
124#endif
125#endif
126 }
127}
static wxLocale * locale_wxLocale
Definition locale_io.cpp:83
static unsigned int locale_count
Definition locale_io.cpp:82
static std::string locale_user_locale
Definition locale_io.cpp:84
static std::mutex locale_mutex
Definition locale_io.cpp:81