1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/gtk1/font.cpp
3// Purpose:
4// Author:      Robert Roebling
5// Id:          $Id: font.cpp 43545 2006-11-20 16:21:08Z VS $
6// Copyright:   (c) 1998 Robert Roebling and Julian Smart
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// ============================================================================
11// declarations
12// ============================================================================
13
14// ----------------------------------------------------------------------------
15// headers
16// ----------------------------------------------------------------------------
17
18// For compilers that support precompilation, includes "wx.h".
19#include "wx/wxprec.h"
20
21#include "wx/font.h"
22
23#ifndef WX_PRECOMP
24    #include "wx/log.h"
25    #include "wx/settings.h"
26    #include "wx/cmndata.h"
27    #include "wx/gdicmn.h"
28#endif
29
30#include "wx/fontutil.h"
31#include "wx/utils.h"
32#include "wx/tokenzr.h"
33
34#include <strings.h>
35
36#include "wx/gtk1/private.h"
37#include <gdk/gdkprivate.h>
38
39// ----------------------------------------------------------------------------
40// constants
41// ----------------------------------------------------------------------------
42
43// the default size (in points) for the fonts
44static const int wxDEFAULT_FONT_SIZE = 12;
45
46// ----------------------------------------------------------------------------
47// wxScaledFontList: maps the font sizes to the GDK fonts for the given font
48// ----------------------------------------------------------------------------
49
50WX_DECLARE_HASH_MAP(int, GdkFont *, wxIntegerHash, wxIntegerEqual,
51                    wxScaledFontList);
52
53// ----------------------------------------------------------------------------
54// wxFontRefData
55// ----------------------------------------------------------------------------
56
57class wxFontRefData : public wxObjectRefData
58{
59public:
60    // from broken down font parameters, also default ctor
61    wxFontRefData(int size = -1,
62                  int family = wxFONTFAMILY_DEFAULT,
63                  int style = wxFONTSTYLE_NORMAL,
64                  int weight = wxFONTWEIGHT_NORMAL,
65                  bool underlined = false,
66                  const wxString& faceName = wxEmptyString,
67                  wxFontEncoding encoding = wxFONTENCODING_DEFAULT);
68
69    // from XFLD
70    wxFontRefData(const wxString& fontname);
71
72    // copy ctor
73    wxFontRefData( const wxFontRefData& data );
74
75    virtual ~wxFontRefData();
76
77    // do we have the native font info?
78    bool HasNativeFont() const
79    {
80        // only use m_nativeFontInfo if it had been initialized
81        return !m_nativeFontInfo.IsDefault();
82    }
83
84    // setters: all of them also take care to modify m_nativeFontInfo if we
85    // have it so as to not lose the information not carried by our fields
86    void SetPointSize(int pointSize);
87    void SetFamily(int family);
88    void SetStyle(int style);
89    void SetWeight(int weight);
90    void SetUnderlined(bool underlined);
91    bool SetFaceName(const wxString& facename);
92    void SetEncoding(wxFontEncoding encoding);
93
94    void SetNoAntiAliasing( bool no = true ) { m_noAA = no; }
95    bool GetNoAntiAliasing() const { return m_noAA; }
96
97    // and this one also modifies all the other font data fields
98    void SetNativeFontInfo(const wxNativeFontInfo& info);
99
100    // debugger helper: shows what the font really is
101    //
102    // VZ: I need this as my gdb either shows wildly wrong values or crashes
103    //     when I ask it to "p fontRefData" :-(
104#if defined(__WXDEBUG__)
105    void Dump() const
106    {
107        wxPrintf(_T("%s-%s-%s-%d-%d\n"),
108                 m_faceName.c_str(),
109                 m_weight == wxFONTWEIGHT_NORMAL
110                    ? _T("normal")
111                    : m_weight == wxFONTWEIGHT_BOLD
112                        ? _T("bold")
113                        : _T("light"),
114                 m_style == wxFONTSTYLE_NORMAL ? _T("regular") : _T("italic"),
115                 m_pointSize,
116                 m_encoding);
117    }
118#endif // Debug
119
120protected:
121    // common part of all ctors
122    void Init(int pointSize,
123              int family,
124              int style,
125              int weight,
126              bool underlined,
127              const wxString& faceName,
128              wxFontEncoding encoding);
129
130    // set all fields from (already initialized and valid) m_nativeFontInfo
131    void InitFromNative();
132
133private:
134    // clear m_scaled_xfonts if any
135    void ClearGdkFonts();
136
137    // the map of font sizes to "GdkFont *"
138    wxScaledFontList  m_scaled_xfonts;
139
140    int             m_pointSize;
141    int             m_family,
142                    m_style,
143                    m_weight;
144    bool            m_underlined;
145    wxString        m_faceName;
146    wxFontEncoding  m_encoding;  // Unused under GTK 2.0
147    bool            m_noAA;      // No anti-aliasing
148
149    // The native font info, basicly an XFLD under GTK 1.2 and
150    // the pango font description under GTK 2.0.
151    wxNativeFontInfo m_nativeFontInfo;
152
153    friend class wxFont;
154};
155
156#define M_FONTDATA ((wxFontRefData*)m_refData)
157
158// ----------------------------------------------------------------------------
159// wxFontRefData
160// ----------------------------------------------------------------------------
161
162void wxFontRefData::Init(int pointSize,
163                         int family,
164                         int style,
165                         int weight,
166                         bool underlined,
167                         const wxString& faceName,
168                         wxFontEncoding encoding)
169{
170    m_family = family == wxFONTFAMILY_DEFAULT ? wxFONTFAMILY_SWISS : family;
171
172    m_faceName = faceName;
173
174    // we accept both wxDEFAULT and wxNORMAL here - should we?
175    m_style = style == wxDEFAULT ? wxFONTSTYLE_NORMAL : style;
176    m_weight = weight == wxDEFAULT ? wxFONTWEIGHT_NORMAL : weight;
177
178    // and here, do we really want to forbid creation of the font of the size
179    // 90 (the value of wxDEFAULT)??
180    m_pointSize = pointSize == wxDEFAULT || pointSize == -1
181                    ? wxDEFAULT_FONT_SIZE
182                    : pointSize;
183
184    m_underlined = underlined;
185    m_encoding = encoding;
186
187    m_noAA = false;
188}
189
190void wxFontRefData::InitFromNative()
191{
192    m_noAA = false;
193
194    // get the font parameters from the XLFD
195    // -------------------------------------
196
197    m_faceName = m_nativeFontInfo.GetXFontComponent(wxXLFD_FAMILY);
198
199    m_weight = wxFONTWEIGHT_NORMAL;
200
201    wxString w = m_nativeFontInfo.GetXFontComponent(wxXLFD_WEIGHT).Upper();
202    if ( !w.empty() && w != _T('*') )
203    {
204        // the test below catches all of BOLD, EXTRABOLD, DEMIBOLD, ULTRABOLD
205        // and BLACK
206        if ( ((w[0u] == _T('B') && (!wxStrcmp(w.c_str() + 1, wxT("OLD")) ||
207                                   !wxStrcmp(w.c_str() + 1, wxT("LACK"))))) ||
208             wxStrstr(w.c_str() + 1, _T("BOLD")) )
209        {
210            m_weight = wxFONTWEIGHT_BOLD;
211        }
212        else if ( w == _T("LIGHT") || w == _T("THIN") )
213        {
214            m_weight = wxFONTWEIGHT_LIGHT;
215        }
216    }
217
218    switch ( wxToupper(*m_nativeFontInfo.
219                            GetXFontComponent(wxXLFD_SLANT).c_str()) )
220    {
221        case _T('I'):   // italique
222            m_style = wxFONTSTYLE_ITALIC;
223            break;
224
225        case _T('O'):   // oblique
226            m_style = wxFONTSTYLE_SLANT;
227            break;
228
229        default:
230            m_style = wxFONTSTYLE_NORMAL;
231    }
232
233    long ptSize;
234    if ( m_nativeFontInfo.GetXFontComponent(wxXLFD_POINTSIZE).ToLong(&ptSize) )
235    {
236        // size in XLFD is in 10 point units
237        m_pointSize = (int)(ptSize / 10);
238    }
239    else
240    {
241        m_pointSize = wxDEFAULT_FONT_SIZE;
242    }
243
244    // examine the spacing: if the font is monospaced, assume wxTELETYPE
245    // family for compatibility with the old code which used it instead of
246    // IsFixedWidth()
247    if ( m_nativeFontInfo.GetXFontComponent(wxXLFD_SPACING).Upper() == _T('M') )
248    {
249        m_family = wxFONTFAMILY_TELETYPE;
250    }
251    else // not monospaceed
252    {
253        // don't even try guessing it, it doesn't work for too many fonts
254        // anyhow
255        m_family = wxFONTFAMILY_UNKNOWN;
256    }
257
258    // X fonts are never underlined...
259    m_underlined = false;
260
261    // deal with font encoding
262    wxString
263        registry = m_nativeFontInfo.GetXFontComponent(wxXLFD_REGISTRY).Upper(),
264        encoding = m_nativeFontInfo.GetXFontComponent(wxXLFD_ENCODING).Upper();
265
266    if ( registry == _T("ISO8859") )
267    {
268        int cp;
269        if ( wxSscanf(encoding, wxT("%d"), &cp) == 1 )
270        {
271            m_encoding = (wxFontEncoding)(wxFONTENCODING_ISO8859_1 + cp - 1);
272        }
273    }
274    else if ( registry == _T("MICROSOFT") )
275    {
276        int cp;
277        if ( wxSscanf(encoding, wxT("cp125%d"), &cp) == 1 )
278        {
279            m_encoding = (wxFontEncoding)(wxFONTENCODING_CP1250 + cp);
280        }
281    }
282    else if ( registry == _T("KOI8") )
283    {
284        m_encoding = wxFONTENCODING_KOI8;
285    }
286    else // unknown encoding
287    {
288        // may be give a warning here? or use wxFontMapper?
289        m_encoding = wxFONTENCODING_SYSTEM;
290    }
291}
292
293wxFontRefData::wxFontRefData( const wxFontRefData& data )
294             : wxObjectRefData()
295{
296    m_pointSize = data.m_pointSize;
297    m_family = data.m_family;
298    m_style = data.m_style;
299    m_weight = data.m_weight;
300
301    m_underlined = data.m_underlined;
302
303    m_faceName = data.m_faceName;
304    m_encoding = data.m_encoding;
305
306    m_noAA = data.m_noAA;
307
308    // Forces a copy of the internal data.  wxNativeFontInfo should probably
309    // have a copy ctor and assignment operator to fix this properly but that
310    // would break binary compatibility...
311    m_nativeFontInfo.FromString(data.m_nativeFontInfo.ToString());
312}
313
314wxFontRefData::wxFontRefData(int size, int family, int style,
315                             int weight, bool underlined,
316                             const wxString& faceName,
317                             wxFontEncoding encoding)
318{
319    Init(size, family, style, weight, underlined, faceName, encoding);
320}
321
322wxFontRefData::wxFontRefData(const wxString& fontname)
323{
324    // FromString() should really work in GTK1 too, doesn't it?
325    m_nativeFontInfo.SetXFontName(fontname);
326
327    InitFromNative();
328}
329
330void wxFontRefData::ClearGdkFonts()
331{
332    for ( wxScaledFontList::iterator i = m_scaled_xfonts.begin();
333          i != m_scaled_xfonts.end();
334          ++i )
335    {
336        GdkFont *font = i->second;
337        gdk_font_unref( font );
338    }
339
340    m_scaled_xfonts.clear();
341}
342
343wxFontRefData::~wxFontRefData()
344{
345    ClearGdkFonts();
346}
347
348// ----------------------------------------------------------------------------
349// wxFontRefData SetXXX()
350// ----------------------------------------------------------------------------
351
352void wxFontRefData::SetPointSize(int pointSize)
353{
354    m_pointSize = pointSize;
355
356    if ( HasNativeFont() )
357    {
358        wxString size;
359        if ( pointSize == -1 )
360            size = _T('*');
361        else
362            size.Printf(_T("%d"), 10*pointSize);
363
364        m_nativeFontInfo.SetXFontComponent(wxXLFD_POINTSIZE, size);
365    }
366}
367
368void wxFontRefData::SetFamily(int family)
369{
370    m_family = family;
371
372    // TODO: what are we supposed to do with m_nativeFontInfo here?
373}
374
375void wxFontRefData::SetStyle(int style)
376{
377    m_style = style;
378
379    if ( HasNativeFont() )
380    {
381        wxString slant;
382        switch ( style )
383        {
384            case wxFONTSTYLE_ITALIC:
385                slant = _T('i');
386                break;
387
388            case wxFONTSTYLE_SLANT:
389                slant = _T('o');
390                break;
391
392            default:
393                wxFAIL_MSG( _T("unknown font style") );
394                // fall through
395
396            case wxFONTSTYLE_NORMAL:
397                slant = _T('r');
398        }
399
400        m_nativeFontInfo.SetXFontComponent(wxXLFD_SLANT, slant);
401    }
402}
403
404void wxFontRefData::SetWeight(int weight)
405{
406    m_weight = weight;
407
408    if ( HasNativeFont() )
409    {
410        wxString boldness;
411        switch ( weight )
412        {
413            case wxFONTWEIGHT_BOLD:
414                boldness = _T("bold");
415                break;
416
417            case wxFONTWEIGHT_LIGHT:
418                boldness = _T("light");
419                break;
420
421            default:
422                wxFAIL_MSG( _T("unknown font weight") );
423                // fall through
424
425            case wxFONTWEIGHT_NORMAL:
426                // unspecified
427                boldness = _T("medium");
428        }
429
430        m_nativeFontInfo.SetXFontComponent(wxXLFD_WEIGHT, boldness);
431    }
432}
433
434void wxFontRefData::SetUnderlined(bool underlined)
435{
436    m_underlined = underlined;
437
438    // the XLFD doesn't have "underlined" field anyhow
439}
440
441bool wxFontRefData::SetFaceName(const wxString& facename)
442{
443    m_faceName = facename;
444
445    if ( HasNativeFont() )
446    {
447        m_nativeFontInfo.SetXFontComponent(wxXLFD_FAMILY, facename);
448    }
449
450    return true;
451}
452
453void wxFontRefData::SetEncoding(wxFontEncoding encoding)
454{
455    m_encoding = encoding;
456
457    if ( HasNativeFont() )
458    {
459        wxNativeEncodingInfo info;
460        if ( wxGetNativeFontEncoding(encoding, &info) )
461        {
462            m_nativeFontInfo.SetXFontComponent(wxXLFD_REGISTRY, info.xregistry);
463            m_nativeFontInfo.SetXFontComponent(wxXLFD_ENCODING, info.xencoding);
464        }
465    }
466}
467
468void wxFontRefData::SetNativeFontInfo(const wxNativeFontInfo& info)
469{
470    // previously cached fonts shouldn't be used
471    ClearGdkFonts();
472
473    m_nativeFontInfo = info;
474
475    // set all the other font parameters from the native font info
476    InitFromNative();
477}
478
479// ----------------------------------------------------------------------------
480// wxFont creation
481// ----------------------------------------------------------------------------
482
483IMPLEMENT_DYNAMIC_CLASS(wxFont, wxGDIObject)
484
485wxFont::wxFont(const wxNativeFontInfo& info)
486{
487    (void) Create(info.GetXFontName());
488}
489
490bool wxFont::Create( int pointSize,
491                     int family,
492                     int style,
493                     int weight,
494                     bool underlined,
495                     const wxString& face,
496                     wxFontEncoding encoding)
497{
498    UnRef();
499
500    m_refData = new wxFontRefData(pointSize, family, style, weight,
501                                  underlined, face, encoding);
502
503    return true;
504}
505
506bool wxFont::Create(const wxString& fontname)
507{
508    // VZ: does this really happen?
509    if ( fontname.empty() )
510    {
511        *this = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
512
513        return true;
514    }
515
516    m_refData = new wxFontRefData(fontname);
517
518    return true;
519}
520
521void wxFont::Unshare()
522{
523    if (!m_refData)
524    {
525        m_refData = new wxFontRefData();
526    }
527    else
528    {
529        wxFontRefData* ref = new wxFontRefData(*(wxFontRefData*)m_refData);
530        UnRef();
531        m_refData = ref;
532    }
533}
534
535wxFont::~wxFont()
536{
537}
538
539// ----------------------------------------------------------------------------
540// accessors
541// ----------------------------------------------------------------------------
542
543int wxFont::GetPointSize() const
544{
545    wxCHECK_MSG( Ok(), 0, wxT("invalid font") );
546
547    return M_FONTDATA->m_pointSize;
548}
549
550wxString wxFont::GetFaceName() const
551{
552    wxCHECK_MSG( Ok(), wxEmptyString, wxT("invalid font") );
553
554    return M_FONTDATA->m_faceName;
555}
556
557int wxFont::GetFamily() const
558{
559    wxCHECK_MSG( Ok(), 0, wxT("invalid font") );
560
561    return M_FONTDATA->m_family;
562}
563
564int wxFont::GetStyle() const
565{
566    wxCHECK_MSG( Ok(), 0, wxT("invalid font") );
567
568    return M_FONTDATA->m_style;
569}
570
571int wxFont::GetWeight() const
572{
573    wxCHECK_MSG( Ok(), 0, wxT("invalid font") );
574
575    return M_FONTDATA->m_weight;
576}
577
578bool wxFont::GetUnderlined() const
579{
580    wxCHECK_MSG( Ok(), false, wxT("invalid font") );
581
582    return M_FONTDATA->m_underlined;
583}
584
585wxFontEncoding wxFont::GetEncoding() const
586{
587    wxCHECK_MSG( Ok(), wxFONTENCODING_DEFAULT, wxT("invalid font") );
588
589    // m_encoding is unused in wxGTK2, return encoding that the user set.
590    return M_FONTDATA->m_encoding;
591}
592
593bool wxFont::GetNoAntiAliasing() const
594{
595    wxCHECK_MSG( Ok(), wxFONTENCODING_DEFAULT, wxT("invalid font") );
596
597    return M_FONTDATA->m_noAA;
598}
599
600const wxNativeFontInfo *wxFont::GetNativeFontInfo() const
601{
602    wxCHECK_MSG( Ok(), (wxNativeFontInfo *)NULL, wxT("invalid font") );
603
604    if ( !M_FONTDATA->HasNativeFont() )
605    {
606        // NB: this call has important side-effect: it not only finds
607        //     GdkFont representation, it also initializes m_nativeFontInfo
608        //     by calling its SetXFontName method
609        GetInternalFont();
610    }
611
612    return &(M_FONTDATA->m_nativeFontInfo);
613}
614
615bool wxFont::IsFixedWidth() const
616{
617    wxCHECK_MSG( Ok(), false, wxT("invalid font") );
618
619    if ( M_FONTDATA->HasNativeFont() )
620    {
621        // the monospace fonts are supposed to have "M" in the spacing field
622        wxString spacing = M_FONTDATA->
623                            m_nativeFontInfo.GetXFontComponent(wxXLFD_SPACING);
624
625        return spacing.Upper() == _T('M');
626    }
627
628    return wxFontBase::IsFixedWidth();
629}
630
631// ----------------------------------------------------------------------------
632// change font attributes
633// ----------------------------------------------------------------------------
634
635void wxFont::SetPointSize(int pointSize)
636{
637    Unshare();
638
639    M_FONTDATA->SetPointSize(pointSize);
640}
641
642void wxFont::SetFamily(int family)
643{
644    Unshare();
645
646    M_FONTDATA->SetFamily(family);
647}
648
649void wxFont::SetStyle(int style)
650{
651    Unshare();
652
653    M_FONTDATA->SetStyle(style);
654}
655
656void wxFont::SetWeight(int weight)
657{
658    Unshare();
659
660    M_FONTDATA->SetWeight(weight);
661}
662
663bool wxFont::SetFaceName(const wxString& faceName)
664{
665    Unshare();
666
667    return M_FONTDATA->SetFaceName(faceName) &&
668           wxFontBase::SetFaceName(faceName);
669}
670
671void wxFont::SetUnderlined(bool underlined)
672{
673    Unshare();
674
675    M_FONTDATA->SetUnderlined(underlined);
676}
677
678void wxFont::SetEncoding(wxFontEncoding encoding)
679{
680    Unshare();
681
682    M_FONTDATA->SetEncoding(encoding);
683}
684
685void wxFont::DoSetNativeFontInfo( const wxNativeFontInfo& info )
686{
687    Unshare();
688
689    M_FONTDATA->SetNativeFontInfo( info );
690}
691
692void wxFont::SetNoAntiAliasing( bool no )
693{
694    Unshare();
695
696    M_FONTDATA->SetNoAntiAliasing( no );
697}
698
699// ----------------------------------------------------------------------------
700// get internal representation of font
701// ----------------------------------------------------------------------------
702
703static GdkFont *g_systemDefaultGuiFont = (GdkFont*) NULL;
704
705// this is also used from tbargtk.cpp and tooltip.cpp, hence extern
706extern GdkFont *GtkGetDefaultGuiFont()
707{
708    if (!g_systemDefaultGuiFont)
709    {
710        GtkWidget *widget = gtk_button_new();
711        GtkStyle *def = gtk_rc_get_style( widget );
712        if (def)
713        {
714            g_systemDefaultGuiFont = gdk_font_ref( def->font );
715        }
716        else
717        {
718            def = gtk_widget_get_default_style();
719            if (def)
720                g_systemDefaultGuiFont = gdk_font_ref( def->font );
721        }
722        gtk_widget_destroy( widget );
723    }
724    else
725    {
726        // already have it, but ref it once more before returning
727        gdk_font_ref(g_systemDefaultGuiFont);
728    }
729
730    return g_systemDefaultGuiFont;
731}
732
733GdkFont *wxFont::GetInternalFont( float scale ) const
734{
735    GdkFont *font = (GdkFont *) NULL;
736
737    wxCHECK_MSG( Ok(), font, wxT("invalid font") );
738
739    long int_scale = long(scale * 100.0 + 0.5); // key for fontlist
740    int point_scale = (int)((M_FONTDATA->m_pointSize * 10 * int_scale) / 100);
741
742    wxScaledFontList& list = M_FONTDATA->m_scaled_xfonts;
743    wxScaledFontList::iterator i = list.find(int_scale);
744    if ( i != list.end() )
745    {
746        font = i->second;
747    }
748    else // we don't have this font in this size yet
749    {
750        if (*this == wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT))
751        {
752            font = GtkGetDefaultGuiFont();
753        }
754
755        if ( !font )
756        {
757            // do we have the XLFD?
758            if ( int_scale == 100 && M_FONTDATA->HasNativeFont() )
759            {
760                font = wxLoadFont(M_FONTDATA->m_nativeFontInfo.GetXFontName());
761            }
762
763            // no XLFD of no exact match - try the approximate one now
764            if ( !font )
765            {
766                wxString xfontname;
767                font = wxLoadQueryNearestFont( point_scale,
768                                               M_FONTDATA->m_family,
769                                               M_FONTDATA->m_style,
770                                               M_FONTDATA->m_weight,
771                                               M_FONTDATA->m_underlined,
772                                               M_FONTDATA->m_faceName,
773                                               M_FONTDATA->m_encoding,
774                                               &xfontname);
775                // NB: wxFont::GetNativeFontInfo relies on this
776                //     side-effect of GetInternalFont
777                if ( int_scale == 100 )
778                    M_FONTDATA->m_nativeFontInfo.SetXFontName(xfontname);
779            }
780        }
781
782        if ( font )
783        {
784            list[int_scale] = font;
785        }
786    }
787
788    // it's quite useless to make it a wxCHECK because we're going to crash
789    // anyhow...
790    wxASSERT_MSG( font, wxT("could not load any font?") );
791
792    return font;
793}
794