Remove all lines containing cvs/svn "$Id$" keyword.
[wxWidgets.git] / src / cocoa / font.mm
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
93 static NSFont* GetNSFontForNativeFontInfo(const wxNativeFontInfo &info);
94 static void UpdateNativeFontInfoWithNSFont(wxNativeFontInfo &info, NSFont *cocoaNSFont);
95 static wxNativeFontInfo MakeNativeFontInfoForNSFont(NSFont *cocoaNSFont, bool underlined = false);
96 static 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  */
113 class WXDLLEXPORT wxFontRefData: public wxGDIRefData
114 {
115     friend class WXDLLIMPEXP_FWD_CORE wxFont;
116 public:
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();
161 protected:
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
196 public:
197 };
198
199 NSString *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)
219 static 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  */
240 static 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  */
270 static 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
283 static 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
296 void 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
301 wxFontRefData::~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
310 wxFont wxCocoaFontFactory::InstanceForNSFont(WX_NSFont cocoaFont, bool underlined)
311 {
312     return wxFont(new wxFontRefData(cocoaFont, underlined));
313 }
314
315 bool wxFont::Create(wxFontRefData *refData)
316 {
317     UnRef();
318     m_refData = refData;
319
320     return m_refData != NULL;
321 }
322
323 bool wxFont::Create(const wxNativeFontInfo& nativeFontInfo)
324 {
325     UnRef();
326     m_refData = new wxFontRefData(nativeFontInfo);
327
328     return true;
329 }
330
331 wxGDIRefData *wxFont::CreateGDIRefData() const
332 {
333     return new wxFontRefData;
334 }
335
336 wxGDIRefData *wxFont::CloneGDIRefData(const wxGDIRefData *data) const
337 {
338     return new wxFontRefData(*static_cast<const wxFontRefData *>(data));
339 }
340
341 void wxFont::SetEncoding(wxFontEncoding)
342 {
343 }
344
345 wxFontEncoding wxFont::GetEncoding() const
346 {
347     return wxFontEncoding();
348 }
349
350 int wxFont::GetPointSize() const
351 {
352     wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
353     return M_FONTDATA->m_info.pointSize;
354 }
355
356 bool wxFont::GetUnderlined() const
357 {
358     if(M_FONTDATA)
359         return M_FONTDATA->m_info.underlined;
360     else
361         return false;
362 }
363
364 wxFontStyle wxFont::GetStyle() const
365 {
366     wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
367     return M_FONTDATA->m_info.style;
368 }
369
370 wxFontFamily wxFont::DoGetFamily() const
371 {
372     return M_FONTDATA->m_info.family;
373 }
374
375 wxFontWeight wxFont::GetWeight() const
376 {
377     wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
378     return M_FONTDATA->m_info.weight;
379 }
380
381 const wxNativeFontInfo *wxFont::GetNativeFontInfo() const
382 {
383     wxCHECK_MSG( IsOk(), 0, wxT("invalid font") );
384     return &M_FONTDATA->m_info;
385 }
386
387 bool 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
397 wxFont::~wxFont()
398 {
399 }
400
401 bool wxFont::RealizeResource()
402 {
403     // TODO: create the font (if there is a native font object)
404     return false;
405 }
406
407 void wxFont::SetPointSize(int pointSize)
408 {
409     AllocExclusive();
410
411     M_FONTDATA->m_info.pointSize = pointSize;
412
413     RealizeResource();
414 }
415
416 void wxFont::SetFamily(wxFontFamily family)
417 {
418     AllocExclusive();
419
420     M_FONTDATA->m_info.family = static_cast<wxFontFamily>(family);
421
422     RealizeResource();
423 }
424
425 void wxFont::SetStyle(wxFontStyle style)
426 {
427     AllocExclusive();
428
429     M_FONTDATA->m_info.style = static_cast<wxFontStyle>(style);
430
431     RealizeResource();
432 }
433
434 void wxFont::SetWeight(wxFontWeight weight)
435 {
436     AllocExclusive();
437
438     M_FONTDATA->m_info.weight = static_cast<wxFontWeight>(weight);
439
440     RealizeResource();
441 }
442
443 bool 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
454 void wxFont::SetUnderlined(bool underlined)
455 {
456     AllocExclusive();
457
458     M_FONTDATA->m_info.underlined = underlined;
459
460     RealizeResource();
461 }
462
463 /* New font system */
464 wxString 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