/////////////////////////////////////////////////////////////////////////////
-// Name: src/cocoa/font.cpp
+// Name: src/cocoa/font.mm
// Purpose: wxFont class
// Author: AUTHOR
// Modified by:
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
+/*! @file font.mm
+ @disucssion
+ Cocoa has three classes which interact to form the font system:
+
+ NSFont: Represents a specific font (e.g. Times-Bold) at a specific size
+ with specific attributes. That is, it's basically like this class.
+ Notably, it doesn't hold an underlined flag, so this class does.
+ Available on all OS X versions.
+
+ NSFontManager: Fairly broad controller class which ties together the
+ model (NSFont) with the view (NSFontPanel). We are ignoring NSFontPanel
+ in this discussion. NSFontManager is actually a little broader than just
+ a controller though. It's also the traditional way of mutating fonts
+ and asking for a font with a certain family and certain attributes.
+ For example, you can use NSFont's factor methods to create a font named
+ @"Times-Roman" then use NSFontManager to imbue attributes like italic and
+ bold. You might do this if, for instance, you already have times at the
+ Roman weight but want to make it bold.
+
+ Alternatively, you can use NSFontManager to ask for a font in the @"Times"
+ family with the bold attribute.
+
+ NSFontManager is available on all OS X versions.
+
+ NSFontDescriptor: Added in OS X 10.3. Prior to this there was no specific
+ class to represent all of the attributes of a font. Instead, a regular
+ NSDictionary was used with a set of well-defined keys. Unfortunately,
+ there was no method accepting the attributes dictionary, only a method
+ to retrieve it from an NSFont. That meant that in order to create
+ a new font by imbueing certain attributes like Bold one would need
+ to use the specific method in NSFontManager to do so.
+
+ The NSFontDescriptor class, on the other hand, has factory methods which
+ can create a new font descriptor with an attributes dictionary as well
+ as mutate (by copy) an existing font descriptor using attributes from
+ an attributes dictionary.
+
+ In theory, most of what can be done using NSFontDescriptor can just as
+ well be done without it. NSFontDescriptor is basically just a shell
+ around an NSMutableDictionary with well-defined keys.
+
+
+ Getting back to the broad overview, font matching is one of the weaker points
+ in Cocoa's API and NSFontDescriptor is the easier to use solution.
+
+ That said, it's not impossible to implement font matching without it. For instance,
+ if you have a family name and want to list available variants (e.g. Bold, italic,
+ underlined) then you can ask NSFontManager for availableMembersOfFontFamily:.
+
+ The problem is that you can either match on family name or on font attributes
+ but not on both. To match both you have to do one then the other.
+ NSFontDescriptor allows you to get a list of fonts matching both a family name
+ and a particular set of attributes. Furthermore, the attributes instead of
+ being flags as in NSFontManager are instead well-defined keys in a dictionary.
+
+ The only way to get that behaviour without NSFontManager is to pare down the
+ list as much as possible using the classic NSFontManager methods and then
+ to instantiate each font in the list and match on each font's afmDictionary.
+
+ A reasonable guess is that that's probably more or less exactly what
+ NSFontDescriptor does internally.
+ */
#include "wx/wxprec.h"
#include "wx/font.h"
#include "wx/fontutil.h"
#include "wx/encinfo.h"
+#include "wx/cocoa/string.h"
+#include "wx/cocoa/private/fontfactory.h"
+#include "wx/cocoa/autorelease.h"
+
+#include <AppKit/NSFont.h>
+#include <AppKit/NSFontManager.h>
+
+// Helper methods for NSFont/wxNativeFontInfo
+static NSFont* GetNSFontForNativeFontInfo(const wxNativeFontInfo &info);
+static void UpdateNativeFontInfoWithNSFont(wxNativeFontInfo &info, NSFont *cocoaNSFont);
+static wxNativeFontInfo MakeNativeFontInfoForNSFont(NSFont *cocoaNSFont, bool underlined = false);
+static wxNativeFontInfo MakeNativeFontInfo(int size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding);
+
+/*! @discussion
+ Due to 2.8 ABI compatibility concerns we probably don't want to change wxNativeFontInfo
+ although this may be unfounded because this class is supposed to be internal as is
+ wxNativeFontInfo so anyone who subclassed it or created one without going through
+ wxFont should expect what they get (i.e. horrible breakage)
+ There's a concern that wxFontRefData was in the public header when 2.8 shipped so
+ it's possible that someone did subclass it to get better font behaviour.
+
+ For right now, the plan is to write it strictly ABI compatible with 2.8 and eventually
+ to enhance it in trunk to accurately represent font attributes as Cocoa sees them.
+
+ I'd like to let at least one 2.8 release go out the door and get feedback as to whether
+ this is going to be a problem or not. If so, we'll keep it strictly ABI compatible.
+ If not, we'll update it.
+ */
class WXDLLEXPORT wxFontRefData: public wxGDIRefData
{
friend class WXDLLIMPEXP_FWD_CORE wxFont;
public:
wxFontRefData()
- : m_fontId(0)
+ : m_cocoaNSFont(nil)
+ , m_info(MakeNativeFontInfo(10, wxDEFAULT, wxNORMAL, wxNORMAL, FALSE,
+ wxT("Geneva"), wxFONTENCODING_DEFAULT))
{
- Init(10, wxDEFAULT, wxNORMAL, wxNORMAL, FALSE,
- wxT("Geneva"), wxFONTENCODING_DEFAULT);
+ CreateNSFontAndUpdateInfo();
}
wxFontRefData(const wxFontRefData& data)
: wxGDIRefData()
- , m_fontId(data.m_fontId)
+ , m_cocoaNSFont([data.m_cocoaNSFont retain])
, m_info(data.m_info)
{
}
+ wxFontRefData(NSFont *cocoaNSFont, bool underlined)
+ : wxGDIRefData()
+ , m_cocoaNSFont([cocoaNSFont retain])
+ , m_info(MakeNativeFontInfoForNSFont(m_cocoaNSFont, underlined))
+ {
+ }
+
+ wxFontRefData(const wxNativeFontInfo& info)
+ : wxGDIRefData()
+ , m_cocoaNSFont(nil)
+ , m_info(info)
+ {
+ CreateNSFontAndUpdateInfo();
+ }
+
wxFontRefData(int size,
- int family,
- int style,
- int weight,
+ wxFontFamily family,
+ wxFontStyle style,
+ wxFontWeight weight,
bool underlined,
const wxString& faceName,
wxFontEncoding encoding)
- : m_fontId(0)
+ : m_cocoaNSFont(nil)
+ , m_info(MakeNativeFontInfo(size, family, style, weight, underlined, faceName, encoding))
{
- Init(size, family, style, weight, underlined, faceName, encoding);
+ CreateNSFontAndUpdateInfo();
}
virtual ~wxFontRefData();
protected:
- // common part of all ctors
+ /*! @abstract common part of some ctors
+ @discussion
+ This is a leftover of sorts from the old stub code.
+ FIXME: Remove from trunk
+ */
void Init(int size,
- int family,
- int style,
- int weight,
+ wxFontFamily family,
+ wxFontStyle style,
+ wxFontWeight weight,
bool underlined,
const wxString& faceName,
wxFontEncoding encoding);
+ /*! @discussion
+ Uses the native font info to create an NSFont and then updates that info with
+ the attributes of the font. This is necessary because, for example, a font
+ can be created with an empty faceName in which case some concrete typeface must
+ be chosen.
+ We choose to handle this by first initializing the wxNativeFontInfo with the
+ properties as given by the user and then creating the NSFont and updating
+ the wxNativeFontInfo to match the NSFont.
+ */
+ void CreateNSFontAndUpdateInfo()
+ {
+ wxAutoNSAutoreleasePool pool;
+ [m_cocoaNSFont release];
+ m_cocoaNSFont = [GetNSFontForNativeFontInfo(m_info) retain];
+ UpdateNativeFontInfoWithNSFont(m_info, m_cocoaNSFont);
+ }
+
// font characterstics
- int m_fontId;
+ NSFont *m_cocoaNSFont;
wxNativeFontInfo m_info;
public:
};
-IMPLEMENT_DYNAMIC_CLASS(wxFont, wxGDIObject)
+NSString *GetFamilyName(wxFontFamily family)
+{
+ switch(family)
+ {
+ case wxFONTFAMILY_DEFAULT:
+ default:
+ return @"Times";
+ case wxFONTFAMILY_DECORATIVE:
+ case wxFONTFAMILY_ROMAN:
+ case wxFONTFAMILY_SCRIPT:
+ return @"Times";
+ case wxFONTFAMILY_SWISS:
+ return @"Lucida Grande";
+ case wxFONTFAMILY_MODERN:
+ case wxFONTFAMILY_TELETYPE:
+ return @"Monaco";
+ };
+}
+// Returns an NSFont given the native font info
+// NOTE: It is not considered alloc'd (i.e. this is a get method not an alloc/new method)
+static NSFont* GetNSFontForNativeFontInfo(const wxNativeFontInfo &info)
+{
+ if(!info.faceName.empty())
+ {
+ NSFont *font = [NSFont fontWithName:wxNSStringWithWxString(info.faceName) size:info.pointSize];
+ // TODO: use NSFontManager to mix in the weights and whatnot
+ if(font != nil)
+ return font;
+ // To err or not to err?
+ }
+ // No font with that face name or no face name
+
+ NSFontTraitMask cocoaTraits = 0;
+ int cocoaWeight = 5;
+ NSFont *font = [[NSFontManager sharedFontManager] fontWithFamily:GetFamilyName(info.family) traits:cocoaTraits weight:cocoaWeight size:info.pointSize];
+ return font;
+}
+
+/*! @discussion
+ Updates all fields of @a info except for underlined which is not a property of NSFont.
+ */
+static void UpdateNativeFontInfoWithNSFont(wxNativeFontInfo &info, NSFont *cocoaNSFont)
+{
+ info.pointSize = [cocoaNSFont pointSize];
+
+ // FIXME: We could maybe improve on this?
+ info.family = wxFONTFAMILY_DEFAULT;
+
+ // FIXME: italicAngle might indicate a slanted rather than truly italic font?
+ info.style = [cocoaNSFont italicAngle] == 0.0?wxFONTSTYLE_NORMAL:wxFONTSTYLE_ITALIC;
+
+ int cocoaWeight = [[NSFontManager sharedFontManager] weightOfFont:cocoaNSFont];
+ if(cocoaWeight < 5)
+ info.weight = wxFONTWEIGHT_LIGHT;
+ else if(cocoaWeight < 9)
+ info.weight = wxFONTWEIGHT_NORMAL;
+ else
+ info.weight = wxFONTWEIGHT_BOLD;
+
+ // FIXME: Is this right? I think so.
+ info.faceName = wxStringWithNSString([cocoaNSFont fontName]);
+
+ // TODO: Translate NSStringEncoding to wxFontEncoding
+ info.encoding = wxFONTENCODING_SYSTEM;
+}
+
+/*! @discussion
+ Creates a new generic wxNativeFontInfo from an NSFont and an underlined flag.
+ Uses UpdateNativeFontInfoWithNSFont to do the work and sets the underlined field
+ of wxNativeFontInfo to the @a underlined argument.
+ */
+static wxNativeFontInfo MakeNativeFontInfoForNSFont(NSFont *cocoaNSFont, bool underlined)
+{
+ wxNativeFontInfo info;
+ UpdateNativeFontInfoWithNSFont(info, cocoaNSFont);
+
+ // NSFont are never underlined.. that's a function of the drawing system
+ info.underlined = underlined;
+
+ return info;
+}
+
+//#include "_font_test_2_8_abi_compat.h"
-void wxFontRefData::Init(int size, int family, int style, int weight, bool underlined, const wxString& faceName, wxFontEncoding encoding)
+static wxNativeFontInfo MakeNativeFontInfo(int size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding)
{
+ wxNativeFontInfo m_info; // NOTE: not an i-var despite name
m_info.pointSize = size;
m_info.family = static_cast<wxFontFamily>(family);
m_info.style = static_cast<wxFontStyle>(style);
m_info.underlined = underlined;
m_info.faceName = faceName;
m_info.encoding = encoding;
+ return m_info;
+}
+
+void wxFontRefData::Init(int size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding)
+{
+ m_info = MakeNativeFontInfo(size, family, style, weight, underlined, faceName, encoding);
}
wxFontRefData::~wxFontRefData()
{
+ [m_cocoaNSFont release];
+ m_cocoaNSFont = nil;
// TODO: delete font data
}
#define M_FONTDATA ((wxFontRefData*)m_refData)
-bool wxFont::Create(const wxNativeFontInfo&)
+wxFont wxCocoaFontFactory::InstanceForNSFont(WX_NSFont cocoaFont, bool underlined)
{
- return false;
+ return wxFont(new wxFontRefData(cocoaFont, underlined));
+}
+
+bool wxFont::Create(wxFontRefData *refData)
+{
+ UnRef();
+ m_refData = refData;
+
+ return m_refData != NULL;
+}
+
+bool wxFont::Create(const wxNativeFontInfo& nativeFontInfo)
+{
+ UnRef();
+ m_refData = new wxFontRefData(nativeFontInfo);
+
+ return true;
+}
+
+wxGDIRefData *wxFont::CreateGDIRefData() const
+{
+ return new wxFontRefData;
+}
+
+wxGDIRefData *wxFont::CloneGDIRefData(const wxGDIRefData *data) const
+{
+ return new wxFontRefData(*static_cast<const wxFontRefData *>(data));
}
void wxFont::SetEncoding(wxFontEncoding)
int wxFont::GetPointSize() const
{
- return 0;
+ wxCHECK_MSG( Ok(), 0, wxT("invalid font") );
+ return M_FONTDATA->m_info.pointSize;
}
bool wxFont::GetUnderlined() const
return false;
}
-int wxFont::GetStyle() const
+wxFontStyle wxFont::GetStyle() const
{
- return 0;
+ wxCHECK_MSG( Ok(), 0, wxT("invalid font") );
+ return M_FONTDATA->m_info.style;
}
-int wxFont::GetFamily() const
+wxFontFamily wxFont::DoGetFamily() const
{
- return 0;
+ return M_FONTDATA->m_info.family;
}
-int wxFont::GetWeight() const
+wxFontWeight wxFont::GetWeight() const
{
- return 0;
+ wxCHECK_MSG( Ok(), 0, wxT("invalid font") );
+ return M_FONTDATA->m_info.weight;
}
const wxNativeFontInfo *wxFont::GetNativeFontInfo() const
return &M_FONTDATA->m_info;
}
-bool wxFont::Create(int pointSize, int family, int style, int weight, bool underlined, const wxString& faceName, wxFontEncoding encoding)
+bool wxFont::Create(int pointSize, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underlined, const wxString& faceName, wxFontEncoding encoding)
{
UnRef();
m_refData = new wxFontRefData(pointSize, family, style, weight, underlined, faceName, encoding);
return false;
}
-void wxFont::Unshare()
-{
- // Don't change shared data
- if (!m_refData)
- {
- m_refData = new wxFontRefData();
- }
- else
- {
- wxFontRefData* ref = new wxFontRefData(*(wxFontRefData*)m_refData);
- UnRef();
- m_refData = ref;
- }
-}
-
void wxFont::SetPointSize(int pointSize)
{
- Unshare();
+ AllocExclusive();
M_FONTDATA->m_info.pointSize = pointSize;
RealizeResource();
}
-void wxFont::SetFamily(int family)
+void wxFont::SetFamily(wxFontFamily family)
{
- Unshare();
+ AllocExclusive();
M_FONTDATA->m_info.family = static_cast<wxFontFamily>(family);
RealizeResource();
}
-void wxFont::SetStyle(int style)
+void wxFont::SetStyle(wxFontStyle style)
{
- Unshare();
+ AllocExclusive();
M_FONTDATA->m_info.style = static_cast<wxFontStyle>(style);
RealizeResource();
}
-void wxFont::SetWeight(int weight)
+void wxFont::SetWeight(wxFontWeight weight)
{
- Unshare();
+ AllocExclusive();
M_FONTDATA->m_info.weight = static_cast<wxFontWeight>(weight);
bool wxFont::SetFaceName(const wxString& faceName)
{
- Unshare();
+ AllocExclusive();
M_FONTDATA->m_info.faceName = faceName;
void wxFont::SetUnderlined(bool underlined)
{
- Unshare();
+ AllocExclusive();
M_FONTDATA->m_info.underlined = underlined;