1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/cocoa/font.mm
3// Purpose:     wxFont class
4// Author:      AUTHOR
5// Modified by:
6// Created:     ??/??/98
7// Copyright:   (c) AUTHOR
8// Licence:     wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11/*! @file   font.mm
12    @disucssion
13    Cocoa has three classes which interact to form the font system:
14
15    NSFont: Represents a specific font (e.g. Times-Bold) at a specific size
16    with specific attributes.  That is, it's basically like this class.
17    Notably, it doesn't hold an underlined flag, so this class does.
18    Available on all OS X versions.
19
20    NSFontManager: Fairly broad controller class which ties together the
21    model (NSFont) with the view (NSFontPanel).  We are ignoring NSFontPanel
22    in this discussion.  NSFontManager is actually a little broader than just
23    a controller though.  It's also the traditional way of mutating fonts
24    and asking for a font with a certain family and certain attributes.
25    For example, you can use NSFont's factor methods to create a font named
26    @"Times-Roman" then use NSFontManager to imbue attributes like italic and
27    bold.  You might do this if, for instance, you already have times at the
28    Roman weight but want to make it bold.
29
30    Alternatively, you can use NSFontManager to ask for a font in the @"Times"
31    family with the bold attribute.
32
33    NSFontManager is available on all OS X versions.
34
35    NSFontDescriptor: Added in OS X 10.3.  Prior to this there was no specific
36    class to represent all of the attributes of a font.  Instead, a regular
37    NSDictionary was used with a set of well-defined keys.  Unfortunately,
38    there was no method accepting the attributes dictionary, only a method
39    to retrieve it from an NSFont.  That meant that in order to create
40    a new font by imbueing certain attributes like Bold one would need
41    to use the specific method in NSFontManager to do so.
42
43    The NSFontDescriptor class, on the other hand, has factory methods which
44    can create a new font descriptor with an attributes dictionary as well
45    as mutate (by copy) an existing font descriptor using attributes from
46    an attributes dictionary.
47
48    In theory, most of what can be done using NSFontDescriptor can just as
49    well be done without it.  NSFontDescriptor is basically just a shell
50    around an NSMutableDictionary with well-defined keys.
51
52
53    Getting back to the broad overview, font matching is one of the weaker points
54    in Cocoa's API and NSFontDescriptor is the easier to use solution.
55
56    That said, it's not impossible to implement font matching without it. For instance,
57    if you have a family name and want to list available variants (e.g. Bold, italic,
58    underlined) then you can ask NSFontManager for availableMembersOfFontFamily:.
59
60    The problem is that you can either match on family name or on font attributes
61    but not on both.  To match both you have to do one then the other.
62    NSFontDescriptor allows you to get a list of fonts matching both a family name
63    and a particular set of attributes.  Furthermore, the attributes instead of
64    being flags as in NSFontManager are instead well-defined keys in a dictionary.
65
66    The only way to get that behaviour without NSFontManager is to pare down the
67    list as much as possible using the classic NSFontManager methods and then
68    to instantiate each font in the list and match on each font's afmDictionary.
69
70    A reasonable guess is that that's probably more or less exactly what
71    NSFontDescriptor does internally.
72 */
73#include "wx/wxprec.h"
74
75#include "wx/font.h"
76
77#ifndef WX_PRECOMP
78    #include "wx/string.h"
79    #include "wx/gdicmn.h"
80#endif
81
82#include "wx/fontutil.h"
83#include "wx/encinfo.h"
84
85#include "wx/cocoa/string.h"
86#include "wx/cocoa/private/fontfactory.h"
87#include "wx/cocoa/autorelease.h"
88
89#include <AppKit/NSFont.h>
90#include <AppKit/NSFontManager.h>
91
92// Helper methods for NSFont/wxNativeFontInfo
93static NSFont* GetNSFontForNativeFontInfo(const wxNativeFontInfo &info);
94static void UpdateNativeFontInfoWithNSFont(wxNativeFontInfo &info, NSFont *cocoaNSFont);
95static wxNativeFontInfo MakeNativeFontInfoForNSFont(NSFont *cocoaNSFont, bool underlined = false);
96static wxNativeFontInfo MakeNativeFontInfo(int size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding);
97
98/*! @discussion
99    Due to 2.8 ABI compatibility concerns we probably don't want to change wxNativeFontInfo
100    although this may be unfounded because this class is supposed to be internal as is
101    wxNativeFontInfo so anyone who subclassed it or created one without going through
102    wxFont should expect what they get (i.e. horrible breakage)
103    There's a concern that wxFontRefData was in the public header when 2.8 shipped so
104    it's possible that someone did subclass it to get better font behaviour.
105
106    For right now, the plan is to write it strictly ABI compatible with 2.8 and eventually
107    to enhance it in trunk to accurately represent font attributes as Cocoa sees them.
108
109    I'd like to let at least one 2.8 release go out the door and get feedback as to whether
110    this is going to be a problem or not.  If so, we'll keep it strictly ABI compatible.
111    If not, we'll update it.
112 */
113class WXDLLEXPORT wxFontRefData: public wxGDIRefData
114{
115    friend class WXDLLIMPEXP_FWD_CORE wxFont;
116public:
117    wxFontRefData()
118    :   m_cocoaNSFont(nil)
119    ,   m_info(MakeNativeFontInfo(10, wxDEFAULT, wxNORMAL, wxNORMAL, FALSE,
120             wxT("Geneva"), wxFONTENCODING_DEFAULT))
121    {
122        CreateNSFontAndUpdateInfo();
123    }
124
125    wxFontRefData(const wxFontRefData& data)
126    :   wxGDIRefData()
127    ,   m_cocoaNSFont([data.m_cocoaNSFont retain])
128    ,   m_info(data.m_info)
129    {
130    }
131
132    wxFontRefData(NSFont *cocoaNSFont, bool underlined)
133    :   wxGDIRefData()
134    ,   m_cocoaNSFont([cocoaNSFont retain])
135    ,   m_info(MakeNativeFontInfoForNSFont(m_cocoaNSFont, underlined))
136    {
137    }
138
139    wxFontRefData(const wxNativeFontInfo& info)
140    :   wxGDIRefData()
141    ,   m_cocoaNSFont(nil)
142    ,   m_info(info)
143    {
144        CreateNSFontAndUpdateInfo();
145    }
146
147    wxFontRefData(int size,
148                  wxFontFamily family,
149                  wxFontStyle style,
150                  wxFontWeight weight,
151                  bool underlined,
152                  const wxString& faceName,
153                  wxFontEncoding encoding)
154    :   m_cocoaNSFont(nil)
155    ,   m_info(MakeNativeFontInfo(size, family, style, weight, underlined, faceName, encoding))
156    {
157        CreateNSFontAndUpdateInfo();
158    }
159
160    virtual ~wxFontRefData();
161protected:
162    /*! @abstract   common part of some ctors
163        @discussion
164        This is a leftover of sorts from the old stub code.
165        FIXME: Remove from trunk
166     */
167    void Init(int size,
168              wxFontFamily family,
169              wxFontStyle style,
170              wxFontWeight weight,
171              bool underlined,
172              const wxString& faceName,
173              wxFontEncoding encoding);
174
175    /*! @discussion
176        Uses the native font info to create an NSFont and then updates that info with
177        the attributes of the font.  This is necessary because, for example, a font
178        can be created with an empty faceName in which case some concrete typeface must
179        be chosen.
180        We choose to handle this by first initializing the wxNativeFontInfo with the
181        properties as given by the user and then creating the NSFont and updating
182        the wxNativeFontInfo to match the NSFont.
183     */
184    void CreateNSFontAndUpdateInfo()
185    {
186        wxAutoNSAutoreleasePool pool;
187        [m_cocoaNSFont release];
188        m_cocoaNSFont = [GetNSFontForNativeFontInfo(m_info) retain];
189        UpdateNativeFontInfoWithNSFont(m_info, m_cocoaNSFont);
190    }
191
192    // font characterstics
193    NSFont *m_cocoaNSFont;
194    wxNativeFontInfo m_info;
195
196public:
197};
198
199NSString *GetFamilyName(wxFontFamily family)
200{
201    switch(family)
202    {
203    case wxFONTFAMILY_DEFAULT:
204    default:
205        return @"Times";
206    case wxFONTFAMILY_DECORATIVE:
207    case wxFONTFAMILY_ROMAN:
208    case wxFONTFAMILY_SCRIPT:
209        return @"Times";
210    case wxFONTFAMILY_SWISS:
211        return @"Lucida Grande";
212    case wxFONTFAMILY_MODERN:
213    case wxFONTFAMILY_TELETYPE:
214        return @"Monaco";
215    };
216}
217// Returns an NSFont given the native font info
218// NOTE: It is not considered alloc'd (i.e. this is a get method not an alloc/new method)
219static NSFont* GetNSFontForNativeFontInfo(const wxNativeFontInfo &info)
220{
221    if(!info.faceName.empty())
222    {
223        NSFont *font = [NSFont fontWithName:wxNSStringWithWxString(info.faceName) size:info.pointSize];
224        // TODO: use NSFontManager to mix in the weights and whatnot
225        if(font != nil)
226            return font;
227        // To err or not to err?
228    }
229    // No font with that face name or no face name
230
231    NSFontTraitMask cocoaTraits = 0;
232    int cocoaWeight = 5;
233    NSFont *font = [[NSFontManager sharedFontManager] fontWithFamily:GetFamilyName(info.family) traits:cocoaTraits weight:cocoaWeight size:info.pointSize];
234    return font;
235}
236
237/*! @discussion
238    Updates all fields of @a info except for underlined which is not a property of NSFont.
239 */
240static void UpdateNativeFontInfoWithNSFont(wxNativeFontInfo &info, NSFont *cocoaNSFont)
241{
242    info.pointSize = [cocoaNSFont pointSize];
243
244    // FIXME: We could maybe improve on this?
245    info.family = wxFONTFAMILY_DEFAULT;
246
247    // FIXME: italicAngle might indicate a slanted rather than truly italic font?
248    info.style = [cocoaNSFont italicAngle] == 0.0?wxFONTSTYLE_NORMAL:wxFONTSTYLE_ITALIC;
249
250    int cocoaWeight = [[NSFontManager sharedFontManager] weightOfFont:cocoaNSFont];
251    if(cocoaWeight < 5)
252        info.weight = wxFONTWEIGHT_LIGHT;
253    else if(cocoaWeight < 9)
254        info.weight = wxFONTWEIGHT_NORMAL;
255    else
256        info.weight = wxFONTWEIGHT_BOLD;
257
258    // FIXME: Is this right?  I think so.
259    info.faceName = wxStringWithNSString([cocoaNSFont fontName]);
260
261    // TODO: Translate NSStringEncoding to wxFontEncoding
262    info.encoding = wxFONTENCODING_SYSTEM;
263}
264
265/*! @discussion
266    Creates a new generic wxNativeFontInfo from an NSFont and an underlined flag.
267    Uses UpdateNativeFontInfoWithNSFont to do the work and sets the underlined field
268    of wxNativeFontInfo to the @a underlined argument.
269 */
270static wxNativeFontInfo MakeNativeFontInfoForNSFont(NSFont *cocoaNSFont, bool underlined)
271{
272    wxNativeFontInfo info;
273    UpdateNativeFontInfoWithNSFont(info, cocoaNSFont);
274
275    // NSFont are never underlined.. that's a function of the drawing system
276    info.underlined = underlined;
277
278    return info;
279}
280
281//#include "_font_test_2_8_abi_compat.h"
282
283static wxNativeFontInfo MakeNativeFontInfo(int size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding)
284{
285    wxNativeFontInfo m_info; // NOTE: not an i-var despite name
286    m_info.pointSize = size;
287    m_info.family = static_cast<wxFontFamily>(family);
288    m_info.style = static_cast<wxFontStyle>(style);
289    m_info.weight = static_cast<wxFontWeight>(weight);
290    m_info.underlined = underlined;
291    m_info.faceName = faceName;
292    m_info.encoding = encoding;
293    return m_info;
294}
295
296void wxFontRefData::Init(int size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding)
297{
298    m_info = MakeNativeFontInfo(size, family, style, weight, underlined, faceName, encoding);
299}
300
301wxFontRefData::~wxFontRefData()
302{
303    [m_cocoaNSFont release];
304    m_cocoaNSFont = nil;
305    // TODO: delete font data
306}
307
308#define M_FONTDATA ((wxFontRefData*)m_refData)
309
310wxFont wxCocoaFontFactory::InstanceForNSFont(WX_NSFont cocoaFont, bool underlined)
311{
312    return wxFont(new wxFontRefData(cocoaFont, underlined));
313}
314
315bool wxFont::Create(wxFontRefData *refData)
316{
317    UnRef();
318    m_refData = refData;
319
320    return m_refData != NULL;
321}
322
323bool wxFont::Create(const wxNativeFontInfo& nativeFontInfo)
324{
325    UnRef();
326    m_refData = new wxFontRefData(nativeFontInfo);
327
328    return true;
329}
330
331wxGDIRefData *wxFont::CreateGDIRefData() const
332{
333    return new wxFontRefData;
334}
335
336wxGDIRefData *wxFont::CloneGDIRefData(const wxGDIRefData *data) const
337{
338    return new wxFontRefData(*static_cast<const wxFontRefData *>(data));
339}
340
341void wxFont::SetEncoding(wxFontEncoding)
342{
343}
344
345wxFontEncoding wxFont::GetEncoding() const
346{
347    return wxFontEncoding();
348}
349
350int wxFont::GetPointSize() const
351{
352    wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
353    return M_FONTDATA->m_info.pointSize;
354}
355
356bool wxFont::GetUnderlined() const
357{
358    if(M_FONTDATA)
359        return M_FONTDATA->m_info.underlined;
360    else
361        return false;
362}
363
364wxFontStyle wxFont::GetStyle() const
365{
366    wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
367    return M_FONTDATA->m_info.style;
368}
369
370wxFontFamily wxFont::DoGetFamily() const
371{
372    return M_FONTDATA->m_info.family;
373}
374
375wxFontWeight wxFont::GetWeight() const
376{
377    wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
378    return M_FONTDATA->m_info.weight;
379}
380
381const wxNativeFontInfo *wxFont::GetNativeFontInfo() const
382{
383    wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
384    return &M_FONTDATA->m_info;
385}
386
387bool wxFont::Create(int pointSize, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding)
388{
389    UnRef();
390    m_refData = new wxFontRefData(pointSize, family, style, weight, underlined, faceName, encoding);
391
392    RealizeResource();
393
394    return true;
395}
396
397wxFont::~wxFont()
398{
399}
400
401bool wxFont::RealizeResource()
402{
403    // TODO: create the font (if there is a native font object)
404    return false;
405}
406
407void wxFont::SetPointSize(int pointSize)
408{
409    AllocExclusive();
410
411    M_FONTDATA->m_info.pointSize = pointSize;
412
413    RealizeResource();
414}
415
416void wxFont::SetFamily(wxFontFamily family)
417{
418    AllocExclusive();
419
420    M_FONTDATA->m_info.family = static_cast<wxFontFamily>(family);
421
422    RealizeResource();
423}
424
425void wxFont::SetStyle(wxFontStyle style)
426{
427    AllocExclusive();
428
429    M_FONTDATA->m_info.style = static_cast<wxFontStyle>(style);
430
431    RealizeResource();
432}
433
434void wxFont::SetWeight(wxFontWeight weight)
435{
436    AllocExclusive();
437
438    M_FONTDATA->m_info.weight = static_cast<wxFontWeight>(weight);
439
440    RealizeResource();
441}
442
443bool wxFont::SetFaceName(const wxString& faceName)
444{
445    AllocExclusive();
446
447    M_FONTDATA->m_info.faceName = faceName;
448
449    RealizeResource();
450
451    return wxFontBase::SetFaceName(faceName);
452}
453
454void wxFont::SetUnderlined(bool underlined)
455{
456    AllocExclusive();
457
458    M_FONTDATA->m_info.underlined = underlined;
459
460    RealizeResource();
461}
462
463/* New font system */
464wxString wxFont::GetFaceName() const
465{
466    wxString str;
467    if (M_FONTDATA)
468        str = M_FONTDATA->m_info.faceName;
469    return str;
470}
471
472// vim:sts=4:sw=4:et
473