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