diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index f7d4a8b7ce..f07cae2ff2 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -3932,9 +3932,7 @@ bool PropertySet::getBoolValue (const String& keyName, XmlElement* PropertySet::getXmlValue (const String& keyName) const { - XmlDocument doc (getValue (keyName)); - - return doc.getDocumentElement(); + return XmlDocument::parse (getValue (keyName)); } void PropertySet::setValue (const String& keyName, const var& v) @@ -9622,8 +9620,7 @@ const String URL::readEntireTextStream (const bool usePostCommand) const XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const { - XmlDocument doc (readEntireTextStream (usePostCommand)); - return doc.getDocumentElement(); + return XmlDocument::parse (readEntireTextStream (usePostCommand)); } const URL URL::withParameter (const String& parameterName, @@ -14894,15 +14891,27 @@ XmlDocument::XmlDocument (const String& documentText) } XmlDocument::XmlDocument (const File& file) - : ignoreEmptyTextElements (true) + : ignoreEmptyTextElements (true), + inputSource (new FileInputSource (file)) { - inputSource = new FileInputSource (file); } XmlDocument::~XmlDocument() { } +XmlElement* XmlDocument::parse (const File& file) +{ + XmlDocument doc (file); + return doc.getDocumentElement(); +} + +XmlElement* XmlDocument::parse (const String& xmlData) +{ + XmlDocument doc (xmlData); + return doc.getDocumentElement(); +} + void XmlDocument::setInputSource (InputSource* const newSource) throw() { inputSource = newSource; @@ -14913,16 +14922,36 @@ void XmlDocument::setEmptyTextElementsIgnored (const bool shouldBeIgnored) throw ignoreEmptyTextElements = shouldBeIgnored; } -bool XmlDocument::isXmlIdentifierCharSlow (const juce_wchar c) throw() +namespace XmlIdentifierChars { - return CharacterFunctions::isLetterOrDigit (c) - || c == '_' || c == '-' || c == ':' || c == '.'; -} + bool isIdentifierCharSlow (const juce_wchar c) throw() + { + return CharacterFunctions::isLetterOrDigit (c) + || c == '_' || c == '-' || c == ':' || c == '.'; + } -inline bool XmlDocument::isXmlIdentifierChar (const juce_wchar c) const throw() -{ - return (c > 0 && c <= 127) ? identifierLookupTable [(int) c] - : isXmlIdentifierCharSlow (c); + bool isIdentifierChar (const juce_wchar c) throw() + { + static const uint32 legalChars[] = { 0, 0x7ff6000, 0x87fffffe, 0x7fffffe, 0 }; + + return (c < numElementsInArray (legalChars) * 32) ? ((legalChars [c >> 5] & (1 << (c & 31))) != 0) + : isIdentifierCharSlow (c); + } + + /*static void generateIdentifierCharConstants() + { + uint32 n[8]; + zerostruct (n); + for (int i = 0; i < 256; ++i) + if (isIdentifierCharSlow (i)) + n[i >> 5] |= (1 << (i & 31)); + + String s; + for (int i = 0; i < 8; ++i) + s << "0x" << String::toHexString ((int) n[i]) << ", "; + + DBG (s); + }*/ } XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) @@ -14950,9 +14979,6 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle outOfData = false; needToLoadDTD = true; - for (int i = 0; i < 128; ++i) - identifierLookupTable[i] = isXmlIdentifierCharSlow ((juce_wchar) i); - if (textToParse.isEmpty()) { lastError = "not enough input"; @@ -15004,14 +15030,10 @@ const String XmlDocument::getFileContents (const String& filename) const juce_wchar XmlDocument::readNextChar() throw() { if (*input != 0) - { return *input++; - } - else - { - outOfData = true; - return 0; - } + + outOfData = true; + return 0; } int XmlDocument::findNextTokenLength() throw() @@ -15019,7 +15041,7 @@ int XmlDocument::findNextTokenLength() throw() int len = 0; juce_wchar c = *input; - while (isXmlIdentifierChar (c)) + while (XmlIdentifierChars::isIdentifierChar (c)) c = input [++len]; return len; @@ -15225,7 +15247,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) } // get an attribute.. - if (isXmlIdentifierChar (c)) + if (XmlIdentifierChars::isIdentifierChar (c)) { const int attNameLen = findNextTokenLength(); @@ -15565,20 +15587,11 @@ void XmlDocument::readEntity (String& result) const String XmlDocument::expandEntity (const String& ent) { - if (ent.equalsIgnoreCase ("amp")) - return String::charToString ('&'); - - if (ent.equalsIgnoreCase ("quot")) - return String::charToString ('"'); - - if (ent.equalsIgnoreCase ("apos")) - return String::charToString ('\''); - - if (ent.equalsIgnoreCase ("lt")) - return String::charToString ('<'); - - if (ent.equalsIgnoreCase ("gt")) - return String::charToString ('>'); + if (ent.equalsIgnoreCase ("amp")) return String::charToString ('&'); + if (ent.equalsIgnoreCase ("quot")) return String::charToString ('"'); + if (ent.equalsIgnoreCase ("apos")) return String::charToString ('\''); + if (ent.equalsIgnoreCase ("lt")) return String::charToString ('<'); + if (ent.equalsIgnoreCase ("gt")) return String::charToString ('>'); if (ent[0] == '#') { @@ -15728,6 +15741,16 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, const Strin value (value_), next (0) { + #if JUCE_DEBUG + // this checks whether the attribute name string contains any illegals characters.. + for (const juce_wchar* t = name; *t != 0; ++t) + jassert (CharacterFunctions::isLetterOrDigit (*t) || *t == '_' || *t == '-' || *t == ':'); + #endif +} + +inline bool XmlElement::XmlAttributeNode::hasName (const String& nameToMatch) const throw() +{ + return name.equalsIgnoreCase (nameToMatch); } XmlElement::XmlElement (const String& tagName_) throw() @@ -15899,21 +15922,13 @@ namespace XmlOutputFunctions case '<': outputStream << "<"; break; case '\n': - if (changeNewLines) - outputStream << " "; - else - outputStream << (char) character; - - break; - case '\r': - if (changeNewLines) - outputStream << " "; - else + if (! changeNewLines) + { outputStream << (char) character; - - break; - + break; + } + // Note: deliberate fall-through here! default: outputStream << "&#" << ((int) (unsigned int) character) << ';'; break; @@ -15961,8 +15976,7 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, const int attIndent = indentationLevel + tagName.length() + 1; int lineLen = 0; - const XmlAttributeNode* att = attributes; - while (att != 0) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) { if (lineLen > lineWrapLength && indentationLevel >= 0) { @@ -15978,8 +15992,6 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, escapeIllegalXmlChars (outputStream, att->value, true); outputStream.writeByte ('"'); lineLen += (int) (outputStream.getPosition() - startPos); - - att = att->next; } } @@ -16137,29 +16149,23 @@ XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName int XmlElement::getNumAttributes() const throw() { - const XmlAttributeNode* att = attributes; int count = 0; - while (att != 0) - { - att = att->next; + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) ++count; - } return count; } const String& XmlElement::getAttributeName (const int index) const throw() { - const XmlAttributeNode* att = attributes; int count = 0; - while (att != 0) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) { if (count == index) return att->name; - att = att->next; ++count; } @@ -16168,15 +16174,13 @@ const String& XmlElement::getAttributeName (const int index) const throw() const String& XmlElement::getAttributeValue (const int index) const throw() { - const XmlAttributeNode* att = attributes; int count = 0; - while (att != 0) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) { if (count == index) return att->value; - att = att->next; ++count; } @@ -16185,86 +16189,54 @@ const String& XmlElement::getAttributeValue (const int index) const throw() bool XmlElement::hasAttribute (const String& attributeName) const throw() { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return true; - att = att->next; - } - return false; } const String& XmlElement::getStringAttribute (const String& attributeName) const throw() { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return att->value; - att = att->next; - } - return String::empty; } const String XmlElement::getStringAttribute (const String& attributeName, const String& defaultReturnValue) const { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return att->value; - att = att->next; - } - return defaultReturnValue; } int XmlElement::getIntAttribute (const String& attributeName, const int defaultReturnValue) const { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return att->value.getIntValue(); - att = att->next; - } - return defaultReturnValue; } double XmlElement::getDoubleAttribute (const String& attributeName, const double defaultReturnValue) const { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return att->value.getDoubleValue(); - att = att->next; - } - return defaultReturnValue; } bool XmlElement::getBoolAttribute (const String& attributeName, const bool defaultReturnValue) const { - const XmlAttributeNode* att = attributes; - - while (att != 0) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) { - if (att->name.equalsIgnoreCase (attributeName)) + if (att->hasName (attributeName)) { juce_wchar firstChar = att->value[0]; @@ -16277,8 +16249,6 @@ bool XmlElement::getBoolAttribute (const String& attributeName, const bool defau || firstChar == 'T' || firstChar == 'Y'; } - - att = att->next; } return defaultReturnValue; @@ -16288,39 +16258,16 @@ bool XmlElement::compareAttribute (const String& attributeName, const String& stringToCompareAgainst, const bool ignoreCase) const throw() { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) - { - if (ignoreCase) - return att->value.equalsIgnoreCase (stringToCompareAgainst); - else - return att->value == stringToCompareAgainst; - } - - att = att->next; - } + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) + return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst) + : att->value == stringToCompareAgainst; return false; } void XmlElement::setAttribute (const String& attributeName, const String& value) { -#if JUCE_DEBUG - // check the identifier being passed in is legal.. - const juce_wchar* t = attributeName; - while (*t != 0) - { - jassert (CharacterFunctions::isLetterOrDigit (*t) - || *t == '_' - || *t == '-' - || *t == ':'); - ++t; - } -#endif - if (attributes == 0) { attributes = new XmlAttributeNode (attributeName, value); @@ -16331,7 +16278,7 @@ void XmlElement::setAttribute (const String& attributeName, const String& value) for (;;) { - if (att->name.equalsIgnoreCase (attributeName)) + if (att->hasName (attributeName)) { att->value = value; break; @@ -16359,12 +16306,11 @@ void XmlElement::setAttribute (const String& attributeName, const double number) void XmlElement::removeAttribute (const String& attributeName) throw() { - XmlAttributeNode* att = attributes; XmlAttributeNode* lastAtt = 0; - while (att != 0) + for (XmlAttributeNode* att = attributes; att != 0; att = att->next) { - if (att->name.equalsIgnoreCase (attributeName)) + if (att->hasName (attributeName)) { if (lastAtt == 0) attributes = att->next; @@ -16376,7 +16322,6 @@ void XmlElement::removeAttribute (const String& attributeName) throw() } lastAtt = att; - att = att->next; } } @@ -20100,8 +20045,7 @@ bool PropertiesFile::save() e->setAttribute (PropertyFileConstants::nameAttribute, getAllProperties().getAllKeys() [i]); // if the value seems to contain xml, store it as such.. - XmlDocument xmlContent (getAllProperties().getAllValues() [i]); - XmlElement* const childElement = xmlContent.getDocumentElement(); + XmlElement* const childElement = XmlDocument::parse (getAllProperties().getAllValues() [i]); if (childElement != 0) e->addChildElement (childElement); @@ -28191,7 +28135,7 @@ BEGIN_JUCE_NAMESPACE namespace MidiFileHelpers { - static void writeVariableLengthInt (OutputStream& out, unsigned int v) + void writeVariableLengthInt (OutputStream& out, unsigned int v) { unsigned int buffer = v & 0x7F; @@ -28212,7 +28156,7 @@ namespace MidiFileHelpers } } - static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) throw() + bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) throw() { unsigned int ch = (int) ByteOrder::bigEndianInt (data); data += 4; @@ -28254,9 +28198,9 @@ namespace MidiFileHelpers return true; } - static double convertTicksToSeconds (const double time, - const MidiMessageSequence& tempoEvents, - const int timeFormat) + double convertTicksToSeconds (const double time, + const MidiMessageSequence& tempoEvents, + const int timeFormat) { if (timeFormat > 0) { @@ -28341,7 +28285,6 @@ namespace MidiFileHelpers } } }; - } MidiFile::MidiFile() @@ -28559,8 +28502,7 @@ bool MidiFile::writeTo (OutputStream& out) return true; } -void MidiFile::writeTrack (OutputStream& mainOut, - const int trackNum) +void MidiFile::writeTrack (OutputStream& mainOut, const int trackNum) { MemoryOutputStream out; @@ -36127,12 +36069,8 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int stringLength = (int) ByteOrder::littleEndianInt (addBytesToPointer (data, 4)); if (stringLength > 0) - { - XmlDocument doc (String::fromUTF8 (static_cast (data) + 8, - jmin ((sizeInBytes - 8), stringLength))); - - return doc.getDocumentElement(); - } + return XmlDocument::parse (String::fromUTF8 (static_cast (data) + 8, + jmin ((sizeInBytes - 8), stringLength))); } return 0; @@ -40617,9 +40555,7 @@ int Component::getScreenY() const const Point Component::getScreenPosition() const { - return (parentComponent_ != 0) ? parentComponent_->getScreenPosition() + getPosition() - : (flags.hasHeavyweightPeerFlag ? getPeer()->getScreenPosition() - : getPosition()); + return relativePositionToGlobal (Point()); } const Rectangle Component::getScreenBounds() const @@ -40629,20 +40565,7 @@ const Rectangle Component::getScreenBounds() const const Point Component::relativePositionToGlobal (const Point& relativePosition) const { - const Component* c = this; - Point p (relativePosition); - - do - { - if (c->flags.hasHeavyweightPeerFlag) - return c->getPeer()->relativePositionToGlobal (p); - - p += c->getPosition(); - c = c->parentComponent_; - } - while (c != 0); - - return p; + return relativePositionToOtherComponent (0, relativePosition); } const Point Component::globalPositionToRelative (const Point& screenPosition) const @@ -40663,29 +40586,26 @@ const Point Component::globalPositionToRelative (const Point& screenPo const Point Component::relativePositionToOtherComponent (const Component* const targetComponent, const Point& positionRelativeToThis) const { Point p (positionRelativeToThis); + const Component* c = this; - if (targetComponent != 0) + do { - const Component* c = this; + if (c == targetComponent) + return p; - do + if (c->flags.hasHeavyweightPeerFlag) { - if (c == targetComponent) - return p; - - if (c->flags.hasHeavyweightPeerFlag) - { - p = c->getPeer()->relativePositionToGlobal (p); - break; - } - - p += c->getPosition(); - c = c->parentComponent_; + p = c->getPeer()->relativePositionToGlobal (p); + break; } - while (c != 0); - p = targetComponent->globalPositionToRelative (p); + p += c->getPosition(); + c = c->parentComponent_; } + while (c != 0); + + if (targetComponent != 0) + p = targetComponent->globalPositionToRelative (p); return p; } @@ -41198,9 +41118,12 @@ void Component::internalHierarchyChanged() } } -void* Component::runModalLoopCallback (void* userData) +namespace ComponentHelpers { - return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); + void* runModalLoopCallback (void* userData) + { + return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); + } } int Component::runModalLoop() @@ -41209,7 +41132,7 @@ int Component::runModalLoop() { // use a callback so this can be called from non-gui threads return (int) (pointer_sized_int) MessageManager::getInstance() - ->callFunctionOnMessageThread (&runModalLoopCallback, this); + ->callFunctionOnMessageThread (&ComponentHelpers::runModalLoopCallback, this); } if (! isCurrentlyModal()) @@ -41248,7 +41171,7 @@ void Component::exitModalState (const int returnValue) ModalComponentManager::getInstance()->endModal (this, returnValue); flags.currentlyModalFlag = false; - bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } else { @@ -41300,34 +41223,6 @@ Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) throw return ModalComponentManager::getInstance()->getModalComponent (index); } -void Component::bringModalComponentToFront() -{ - ComponentPeer* lastOne = 0; - - for (int i = 0; i < getNumCurrentlyModalComponents(); ++i) - { - Component* const c = getCurrentlyModalComponent (i); - - if (c == 0) - break; - - ComponentPeer* peer = c->getPeer(); - - if (peer != 0 && peer != lastOne) - { - if (lastOne == 0) - { - peer->toFront (true); - peer->grabFocus(); - } - else - peer->toBehind (lastOne); - - lastOne = peer; - } - } -} - void Component::setBroughtToFrontOnMouseClick (const bool shouldBeBroughtToFront) throw() { flags.bringToFrontOnClickFlag = shouldBeBroughtToFront; @@ -41905,7 +41800,7 @@ void Component::removeComponentListener (ComponentListener* const listenerToRemo void Component::inputAttemptWhenModal() { - bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); getLookAndFeel().playAlertSound(); } @@ -42350,7 +42245,7 @@ void Component::internalBroughtToFront() Component* const cm = getCurrentlyModalComponent(); if (cm != 0 && cm->getTopLevelComponent() != getTopLevelComponent()) - bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } void Component::focusGained (FocusChangeType) @@ -43297,6 +43192,34 @@ void ModalComponentManager::handleAsyncUpdate() } } +void ModalComponentManager::bringModalComponentsToFront() +{ + ComponentPeer* lastOne = 0; + + for (int i = 0; i < getNumModalComponents(); ++i) + { + Component* const c = getModalComponent (i); + + if (c == 0) + break; + + ComponentPeer* peer = c->getPeer(); + + if (peer != 0 && peer != lastOne) + { + if (lastOne == 0) + { + peer->toFront (true); + peer->grabFocus(); + } + else + peer->toBehind (lastOne); + + lastOne = peer; + } + } +} + class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback { public: @@ -51398,9 +51321,7 @@ const String TableHeaderComponent::toString() const void TableHeaderComponent::restoreFromString (const String& storedVersion) { - XmlDocument doc (storedVersion); - ScopedPointer storedXml (doc.getDocumentElement()); - + ScopedPointer storedXml (XmlDocument::parse (storedVersion)); int index = 0; if (storedXml != 0 && storedXml->hasTagName ("TABLELAYOUT")) @@ -64472,7 +64393,7 @@ void TabbedComponent::clearTabs() for (int i = contentComponents.size(); --i >= 0;) { - Component::SafePointer& c = contentComponents.getReference (i); + Component::SafePointer& c = *contentComponents.getUnchecked (i); if (c != 0 && (bool) c->getProperties() [deleteComponentId]) c.deleteAndZero(); @@ -64487,7 +64408,7 @@ void TabbedComponent::addTab (const String& tabName, const bool deleteComponentWhenNotNeeded, const int insertIndex) { - contentComponents.insert (insertIndex, contentComponent); + contentComponents.insert (insertIndex, new Component::SafePointer (contentComponent)); if (contentComponent != 0) contentComponent->getProperties().set (deleteComponentId, deleteComponentWhenNotNeeded); @@ -64502,12 +64423,12 @@ void TabbedComponent::setTabName (const int tabIndex, const String& newName) void TabbedComponent::removeTab (const int tabIndex) { - if (tabIndex >= 0 && tabIndex < contentComponents.size()) - { - Component::SafePointer& c = contentComponents.getReference (tabIndex); + Component::SafePointer* c = contentComponents [tabIndex]; - if (c != 0 && (bool) c->getProperties() [deleteComponentId]) - c.deleteAndZero(); + if (c != 0) + { + if ((bool) ((*c)->getProperties() [deleteComponentId])) + c->deleteAndZero(); contentComponents.remove (tabIndex); tabs->removeTab (tabIndex); @@ -64526,7 +64447,8 @@ const StringArray TabbedComponent::getTabNames() const Component* TabbedComponent::getTabContentComponent (const int tabIndex) const throw() { - return contentComponents [tabIndex]; + Component::SafePointer* const c = contentComponents [tabIndex]; + return c != 0 ? *c : 0; } const Colour TabbedComponent::getTabBackgroundColour (const int tabIndex) const throw() @@ -64637,15 +64559,15 @@ void TabbedComponent::resized() const Rectangle bounds (indents.subtractedFrom (getLocalBounds())); for (int i = contentComponents.size(); --i >= 0;) - if (contentComponents.getReference (i) != 0) - contentComponents.getReference (i)->setBounds (bounds); + if (*contentComponents.getUnchecked (i) != 0) + (*contentComponents.getUnchecked (i))->setBounds (bounds); } void TabbedComponent::lookAndFeelChanged() { for (int i = contentComponents.size(); --i >= 0;) - if (contentComponents.getReference (i) != 0) - contentComponents.getReference (i)->lookAndFeelChanged(); + if (*contentComponents.getUnchecked (i) != 0) + (*contentComponents.getUnchecked (i))->lookAndFeelChanged(); } void TabbedComponent::changeCallback (const int newCurrentTabIndex, @@ -64660,7 +64582,7 @@ void TabbedComponent::changeCallback (const int newCurrentTabIndex, if (getCurrentTabIndex() >= 0) { - panelComponent = contentComponents [getCurrentTabIndex()]; + panelComponent = getTabContentComponent (getCurrentTabIndex()); if (panelComponent != 0) { @@ -65277,14 +65199,15 @@ LookAndFeel::LookAndFeel() for (int i = 0; i < numElementsInArray (standardColours); i += 2) setColour (standardColours [i], Colour (standardColours [i + 1])); - static String defaultSansName, defaultSerifName, defaultFixedName; + static String defaultSansName, defaultSerifName, defaultFixedName, defaultFallback; if (defaultSansName.isEmpty()) - Font::getPlatformDefaultFontNames (defaultSansName, defaultSerifName, defaultFixedName); + Font::getPlatformDefaultFontNames (defaultSansName, defaultSerifName, defaultFixedName, defaultFallback); defaultSans = defaultSansName; defaultSerif = defaultSerifName; defaultFixed = defaultFixedName; + Font::setFallbackFontName (defaultFallback); } LookAndFeel::~LookAndFeel() @@ -69231,10 +69154,10 @@ private: namespace PopupMenuSettings { - static const int scrollZone = 24; - static const int borderSize = 2; - static const int timerInterval = 50; - static const int dismissCommandId = 0x6287345f; + const int scrollZone = 24; + const int borderSize = 2; + const int timerInterval = 50; + const int dismissCommandId = 0x6287345f; } class PopupMenu::Window : public Component, @@ -69372,9 +69295,8 @@ public: bool isScrollZoneActive (bool bottomOne) const { return isScrolling() - && (bottomOne - ? childYOffset < contentHeight - windowPos.getHeight() - : childYOffset > 0); + && (bottomOne ? childYOffset < contentHeight - windowPos.getHeight() + : childYOffset > 0); } void addItem (const PopupMenu::Item& item) @@ -69432,25 +69354,10 @@ public: } } - void mouseMove (const MouseEvent&) - { - timerCallback(); - } - - void mouseDown (const MouseEvent&) - { - timerCallback(); - } - - void mouseDrag (const MouseEvent&) - { - timerCallback(); - } - - void mouseUp (const MouseEvent&) - { - timerCallback(); - } + void mouseMove (const MouseEvent&) { timerCallback(); } + void mouseDown (const MouseEvent&) { timerCallback(); } + void mouseDrag (const MouseEvent&) { timerCallback(); } + void mouseUp (const MouseEvent&) { timerCallback(); } void mouseWheelMove (const MouseEvent&, float /*amountX*/, float amountY) { @@ -70126,12 +70033,9 @@ private: } Path areaTowardsSubMenu; - areaTowardsSubMenu.addTriangle ((float) lastMouse.getX(), - (float) lastMouse.getY(), - subX, - (float) activeSubMenu->getScreenY(), - subX, - (float) (activeSubMenu->getScreenY() + activeSubMenu->getHeight())); + areaTowardsSubMenu.addTriangle ((float) lastMouse.getX(), (float) lastMouse.getY(), + subX, (float) activeSubMenu->getScreenY(), + subX, (float) (activeSubMenu->getScreenY() + activeSubMenu->getHeight())); isMovingTowardsMenu = areaTowardsSubMenu.contains ((float) globalMousePos.getX(), (float) globalMousePos.getY()); } @@ -70261,11 +70165,8 @@ void PopupMenu::addSeparatorIfPending() } } -void PopupMenu::addItem (const int itemResultId, - const String& itemText, - const bool isActive, - const bool isTicked, - const Image& iconToUse) +void PopupMenu::addItem (const int itemResultId, const String& itemText, + const bool isActive, const bool isTicked, const Image& iconToUse) { jassert (itemResultId != 0); // 0 is used as a return value to indicate that the user // didn't pick anything, so you shouldn't use it as the id @@ -70338,12 +70239,10 @@ void PopupMenu::addCustomItem (const int itemResultId, class NormalComponentWrapper : public PopupMenuCustomComponent { public: - NormalComponentWrapper (Component* const comp, - const int w, const int h, + NormalComponentWrapper (Component* const comp, const int w, const int h, const bool triggerMenuItemAutomaticallyWhenClicked) : PopupMenuCustomComponent (triggerMenuItemAutomaticallyWhenClicked), - width (w), - height (h) + width (w), height (h) { addAndMakeVisible (comp); } @@ -70377,8 +70276,7 @@ void PopupMenu::addCustomItem (const int itemResultId, const bool triggerMenuItemAutomaticallyWhenClicked) { addCustomItem (itemResultId, - new NormalComponentWrapper (customComponent, - idealWidth, idealHeight, + new NormalComponentWrapper (customComponent, idealWidth, idealHeight, triggerMenuItemAutomaticallyWhenClicked)); } @@ -70449,8 +70347,6 @@ public: { } - ~PopupMenuCompletionCallback() {} - void modalStateFinished (int result) { if (managerOfChosenCommand != 0 && result != 0) @@ -70525,8 +70421,7 @@ int PopupMenu::showMenu (const Rectangle& target, } int PopupMenu::show (const int itemIdThatMustBeVisible, - const int minimumWidth, - const int maximumNumColumns, + const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, ModalComponentManager::Callback* callback) { @@ -70534,31 +70429,24 @@ int PopupMenu::show (const int itemIdThatMustBeVisible, return showAt (mousePos.getX(), mousePos.getY(), itemIdThatMustBeVisible, - minimumWidth, - maximumNumColumns, - standardItemHeight, - callback); + minimumWidth, maximumNumColumns, + standardItemHeight, callback); } -int PopupMenu::showAt (const int screenX, - const int screenY, +int PopupMenu::showAt (const int screenX, const int screenY, const int itemIdThatMustBeVisible, - const int minimumWidth, - const int maximumNumColumns, + const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, ModalComponentManager::Callback* callback) { return showMenu (Rectangle (screenX, screenY, 1, 1), - itemIdThatMustBeVisible, - minimumWidth, maximumNumColumns, - standardItemHeight, - false, 0, callback); + itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, + standardItemHeight, false, 0, callback); } int PopupMenu::showAt (Component* componentToAttachTo, const int itemIdThatMustBeVisible, - const int minimumWidth, - const int maximumNumColumns, + const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, ModalComponentManager::Callback* callback) { @@ -70574,10 +70462,8 @@ int PopupMenu::showAt (Component* componentToAttachTo, else { return show (itemIdThatMustBeVisible, - minimumWidth, - maximumNumColumns, - standardItemHeight, - callback); + minimumWidth, maximumNumColumns, + standardItemHeight, callback); } } @@ -78021,7 +77907,7 @@ void ComponentPeer::handleFocusGain() if (! component->isCurrentlyBlockedByAnotherModalComponent()) component->grabKeyboardFocus(); else - Component::bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } } @@ -78173,15 +78059,9 @@ void ComponentPeer::handleFileDragDrop (const StringArray& files, const PointuserTriedToCloseWindow(); } -void ComponentPeer::bringModalComponentToFront() -{ - Component::bringModalComponentToFront(); -} - void ComponentPeer::clearMaskedRegion() { maskedRegion.clear(); @@ -78252,10 +78132,6 @@ public: setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level } - ~TempDialogWindow() - { - } - void closeButtonPressed() { setVisible (false); @@ -78264,7 +78140,6 @@ public: private: TempDialogWindow (const TempDialogWindow&); TempDialogWindow& operator= (const TempDialogWindow&); - }; int DialogWindow::showModalDialog (const String& dialogTitle, @@ -89582,14 +89457,13 @@ BEGIN_JUCE_NAMESPACE namespace FontValues { - static float limitFontHeight (const float height) throw() + float limitFontHeight (const float height) throw() { return jlimit (0.1f, 10000.0f, height); } - static const float defaultFontHeight = 14.0f; - - static String fallbackFont; + const float defaultFontHeight = 14.0f; + String fallbackFont; } Font::SharedFontInternal::SharedFontInternal (const String& typefaceName_, const float height_, const float horizontalScale_, @@ -91145,7 +91019,7 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE Typeface::Typeface (const String& name_) throw() - : name (name_) + : name (name_), isFallbackFont (false) { } @@ -91153,6 +91027,14 @@ Typeface::~Typeface() { } +const Typeface::Ptr Typeface::getFallbackTypeface() +{ + const Font fallbackFont (Font::getFallbackFontName(), 10, 0); + Typeface* t = fallbackFont.getTypeface(); + t->isFallbackFont = true; + return t; +} + class CustomTypeface::GlyphInfo { public: @@ -91326,7 +91208,9 @@ CustomTypeface::GlyphInfo* CustomTypeface::findGlyphSubstituting (const juce_wch Typeface* const fallbackTypeface = fallbackFont.getTypeface(); if (fallbackTypeface != 0 && fallbackTypeface != this) { - //xxx + Path path; + fallbackTypeface->getOutlineForGlyph (character, path); + addGlyph (character, path, fallbackTypeface->getStringWidth (String::charToString (character))); } if (glyph == 0) @@ -91439,6 +91323,14 @@ float CustomTypeface::getStringWidth (const String& text) { const GlyphInfo* const glyph = findGlyphSubstituting (*t++); + if (glyph == 0 && ! isFallbackFont) + { + const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); + + if (fallbackTypeface != 0) + x += fallbackTypeface->getStringWidth (String::charToString (*t)); + } + if (glyph != 0) x += glyph->getHorizontalSpacing (*t); } @@ -91455,7 +91347,26 @@ void CustomTypeface::getGlyphPositions (const String& text, Array & resultG while (*t != 0) { const juce_wchar c = *t++; - const GlyphInfo* const glyph = findGlyphSubstituting (c); + const GlyphInfo* const glyph = findGlyph (c, true); + + if (glyph == 0 && ! isFallbackFont) + { + const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); + + if (fallbackTypeface != 0) + { + Array subGlyphs; + Array subOffsets; + fallbackTypeface->getGlyphPositions (String::charToString (c), subGlyphs, subOffsets); + + if (subGlyphs.size() > 0) + { + resultGlyphs.add (subGlyphs.getFirst()); + x += subOffsets[1]; + xOffsets.add (x); + } + } + } if (glyph != 0) { @@ -91468,7 +91379,16 @@ void CustomTypeface::getGlyphPositions (const String& text, Array & resultG bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path) { - const GlyphInfo* const glyph = findGlyphSubstituting ((juce_wchar) glyphNumber); + const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true); + + if (glyph == 0 && ! isFallbackFont) + { + const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); + + if (fallbackTypeface != 0) + fallbackTypeface->getOutlineForGlyph (glyphNumber, path); + } + if (glyph != 0) { path = glyph->path; @@ -240407,7 +240327,7 @@ const StringArray Font::findAllTypefaceNames() extern bool juce_IsRunningInWine(); -void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback) { if (juce_IsRunningInWine()) { @@ -240418,9 +240338,10 @@ void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSeri } else { - defaultSans = "Verdana"; - defaultSerif = "Times"; - defaultFixed = "Lucida Console"; + defaultSans = "Verdana"; + defaultSerif = "Times"; + defaultFixed = "Lucida Console"; + defaultFallback = "Tahoma"; // (contains plenty of unicode characters) } } @@ -240570,6 +240491,10 @@ public: GLYPHMETRICS gm; + // if this is the fallback font, skip checking for the glyph's existence. This is because + // with fonts like Tahoma, GetGlyphIndices can say that a glyph doesn't exist, but it still + // gets correctly created later on. + if (! isFallbackFont) { const WCHAR charToTest[] = { (WCHAR) character, 0 }; WORD index = 0; @@ -257373,8 +257298,7 @@ public: if (fontDirs.size() == 0) { - XmlDocument fontsConfig (File ("/etc/fonts/fonts.conf")); - const ScopedPointer fontsInfo (fontsConfig.getDocumentElement()); + const ScopedPointer fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf"))); if (fontsInfo != 0) { @@ -257823,7 +257747,7 @@ namespace } } -void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& /*defaultFallback*/) { defaultSans = linux_getDefaultSansSerifFontName(); defaultSerif = linux_getDefaultSerifFontName(); @@ -266665,7 +266589,7 @@ const StringArray Font::findAllTypefaceNames() return names; } -void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback) { #if JUCE_IOS defaultSans = "Helvetica"; @@ -266676,6 +266600,8 @@ void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSeri defaultSerif = "Times New Roman"; defaultFixed = "Monaco"; #endif + + defaultFallback = "Arial Unicode MS"; } #endif @@ -268242,7 +268168,7 @@ void juce_HandleProcessFocusChange() { currentlyFocusedPeer->handleFocusGain(); - ComponentPeer::bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } else { @@ -271389,7 +271315,7 @@ const StringArray Font::findAllTypefaceNames() return names; } -void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback) { #if JUCE_IOS defaultSans = "Helvetica"; @@ -271400,6 +271326,8 @@ void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSeri defaultSerif = "Times New Roman"; defaultFixed = "Monaco"; #endif + + defaultFallback = "Arial Unicode MS"; } #endif @@ -273457,7 +273385,7 @@ void juce_HandleProcessFocusChange() { NSViewComponentPeer::currentlyFocusedPeer->handleFocusGain(); - ComponentPeer::bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } else { diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 0e5615f6bb..48da2fb7ae 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -64,7 +64,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 86 +#define JUCE_BUILDNUMBER 87 /** Current Juce version number. @@ -1394,41 +1394,41 @@ private: inline uint16 ByteOrder::swap (uint16 n) { -#if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! + #if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! return static_cast (_byteswap_ushort (n)); -#else + #else return static_cast ((n << 8) | (n >> 8)); -#endif + #endif } inline uint32 ByteOrder::swap (uint32 n) { -#if JUCE_MAC || JUCE_IOS + #if JUCE_MAC || JUCE_IOS return OSSwapInt32 (n); -#elif JUCE_GCC + #elif JUCE_GCC asm("bswap %%eax" : "=a"(n) : "a"(n)); return n; -#elif JUCE_USE_INTRINSICS + #elif JUCE_USE_INTRINSICS return _byteswap_ulong (n); -#else + #else __asm { mov eax, n bswap eax mov n, eax } return n; -#endif + #endif } inline uint64 ByteOrder::swap (uint64 value) { -#if JUCE_MAC || JUCE_IOS + #if JUCE_MAC || JUCE_IOS return OSSwapInt64 (value); -#elif JUCE_USE_INTRINSICS + #elif JUCE_USE_INTRINSICS return _byteswap_uint64 (value); -#else + #else return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); -#endif + #endif } #if JUCE_LITTLE_ENDIAN @@ -10294,6 +10294,8 @@ private: String name, value; XmlAttributeNode* next; + bool hasName (const String& name) const throw(); + private: XmlAttributeNode& operator= (const XmlAttributeNode&); }; @@ -17680,6 +17682,16 @@ private: @endcode + Or you can use the static helper methods for quick parsing.. + + @code + XmlElement* xml = XmlDocument::parse (myXmlFile); + + if (xml != 0 && xml->hasTagName ("foobar")) + { + ...etc + @endcode + @see XmlElement */ class JUCE_API XmlDocument @@ -17687,16 +17699,12 @@ class JUCE_API XmlDocument public: /** Creates an XmlDocument from the xml text. - - The text doesn't actually get parsed until the getDocumentElement() method is - called. + The text doesn't actually get parsed until the getDocumentElement() method is called. */ XmlDocument (const String& documentText); /** Creates an XmlDocument from a file. - - The text doesn't actually get parsed until the getDocumentElement() method is - called. + The text doesn't actually get parsed until the getDocumentElement() method is called. */ XmlDocument (const File& file); @@ -17709,6 +17717,9 @@ public: parse error, it may returns 0 (and you can find out the error using the getLastParseError() method). + See also the parse() methods, which provide a shorthand way to quickly + parse a file or string. + @param onlyReadOuterDocumentElement if true, the parser will only read the first section of the file, and will only return the outer document element - this @@ -17749,6 +17760,18 @@ public: */ void setEmptyTextElementsIgnored (bool shouldBeIgnored) throw(); + /** A handy static method that parses a file. + This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it. + @returns a new XmlElement which the caller will need to delete, or null if there was an error. + */ + static XmlElement* parse (const File& file); + + /** A handy static method that parses some XML data. + This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it. + @returns a new XmlElement which the caller will need to delete, or null if there was an error. + */ + static XmlElement* parse (const String& xmlData); + juce_UseDebuggingNewOperator private: @@ -17756,7 +17779,6 @@ private: const juce_wchar* input; bool outOfData, errorOccurred; - bool identifierLookupTable [128]; String lastError, dtdText; StringArray tokenisedDTD; bool needToLoadDTD, ignoreEmptyTextElements; @@ -17771,8 +17793,6 @@ private: int findNextTokenLength() throw(); void readQuotedString (String& result); void readEntity (String& result); - static bool isXmlIdentifierCharSlow (juce_wchar c) throw(); - bool isXmlIdentifierChar (juce_wchar c) const throw(); const String getFileContents (const String& filename) const; const String expandEntity (const String& entity); @@ -22624,9 +22644,12 @@ public: protected: String name; + bool isFallbackFont; explicit Typeface (const String& name) throw(); + static const Ptr getFallbackTypeface(); + private: Typeface (const Typeface&); Typeface& operator= (const Typeface&); @@ -22873,7 +22896,7 @@ public: static const String getDefaultMonospacedFontName(); /** Returns the typeface names of the default fonts on the current platform. */ - static void getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed); + static void getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback); /** Returns the total height of this font. @@ -25951,6 +25974,9 @@ public: */ void attachCallback (Component* component, Callback* callback); + /** Brings any modal components to the front. */ + void bringModalComponentsToFront(); + /** Runs the event loop until the currently topmost modal component is dismissed, and returns the exit code for that component. */ @@ -26272,20 +26298,19 @@ public: const Rectangle getScreenBounds() const; /** Converts a position relative to this component's top-left into a screen co-ordinate. - @see globalPositionToRelative, relativePositionToOtherComponent */ const Point relativePositionToGlobal (const Point& relativePosition) const; /** Converts a screen co-ordinate into a position relative to this component's top-left. - @see relativePositionToGlobal, relativePositionToOtherComponent */ const Point globalPositionToRelative (const Point& screenPosition) const; /** Converts a position relative to this component's top-left into a position relative to another component's top-left. - + If the targetComponent parameter is null, the coordinate is converted to global screen + coordinates. @see relativePositionToGlobal, globalPositionToRelative */ const Point relativePositionToOtherComponent (const Component* targetComponent, @@ -27133,7 +27158,8 @@ public: if you want to force the system to check that the cursor being displayed is up-to-date (even if the mouse is just sitting there), call this method. - This isn't needed if you're only using setMouseCursor(). + (If you're changing the cursor using setMouseCursor(), you don't need to bother + calling this). */ void updateMouseCursor() const; @@ -27968,8 +27994,6 @@ private: void grabFocusInternal (const FocusChangeType cause, bool canTryParent = true); static void giveAwayFocus(); void sendEnablementChangeMessage(); - static void* runModalLoopCallback (void*); - static void bringModalComponentToFront(); void subtractObscuredRegions (RectangleList& result, const Point& delta, const Rectangle& clipRect, const Component* const compToAvoid) const; void clipObscuredRegions (Graphics& g, const Rectangle& clipRect, int deltaX, int deltaY) const; @@ -53251,7 +53275,7 @@ protected: private: - Array > contentComponents; + OwnedArray > contentComponents; Component::SafePointer panelComponent; int tabDepth; int outlineThickness, edgeIndent; @@ -59264,8 +59288,6 @@ public: */ static bool isValidPeer (const ComponentPeer* peer) throw(); - static void bringModalComponentToFront(); - virtual const StringArray getAvailableRenderingEngines(); virtual int getCurrentRenderingEngine() throw(); virtual void setCurrentRenderingEngine (int index); diff --git a/src/core/juce_ByteOrder.h b/src/core/juce_ByteOrder.h index b7f8108aaf..d3f82ca07b 100644 --- a/src/core/juce_ByteOrder.h +++ b/src/core/juce_ByteOrder.h @@ -107,41 +107,41 @@ private: inline uint16 ByteOrder::swap (uint16 n) { -#if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! + #if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! return static_cast (_byteswap_ushort (n)); -#else + #else return static_cast ((n << 8) | (n >> 8)); -#endif + #endif } inline uint32 ByteOrder::swap (uint32 n) { -#if JUCE_MAC || JUCE_IOS + #if JUCE_MAC || JUCE_IOS return OSSwapInt32 (n); -#elif JUCE_GCC + #elif JUCE_GCC asm("bswap %%eax" : "=a"(n) : "a"(n)); return n; -#elif JUCE_USE_INTRINSICS + #elif JUCE_USE_INTRINSICS return _byteswap_ulong (n); -#else + #else __asm { mov eax, n bswap eax mov n, eax } return n; -#endif + #endif } inline uint64 ByteOrder::swap (uint64 value) { -#if JUCE_MAC || JUCE_IOS + #if JUCE_MAC || JUCE_IOS return OSSwapInt64 (value); -#elif JUCE_USE_INTRINSICS + #elif JUCE_USE_INTRINSICS return _byteswap_uint64 (value); -#else + #else return (((int64) swap ((uint32) value)) << 32) | swap ((uint32) (value >> 32)); -#endif + #endif } #if JUCE_LITTLE_ENDIAN diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 6dba1c01f9..458d486472 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 86 +#define JUCE_BUILDNUMBER 87 /** Current Juce version number. diff --git a/src/gui/components/juce_Component.cpp b/src/gui/components/juce_Component.cpp index 99ce3d0e28..f9836b6a2e 100644 --- a/src/gui/components/juce_Component.cpp +++ b/src/gui/components/juce_Component.cpp @@ -777,9 +777,7 @@ int Component::getScreenY() const const Point Component::getScreenPosition() const { - return (parentComponent_ != 0) ? parentComponent_->getScreenPosition() + getPosition() - : (flags.hasHeavyweightPeerFlag ? getPeer()->getScreenPosition() - : getPosition()); + return relativePositionToGlobal (Point()); } const Rectangle Component::getScreenBounds() const @@ -789,20 +787,7 @@ const Rectangle Component::getScreenBounds() const const Point Component::relativePositionToGlobal (const Point& relativePosition) const { - const Component* c = this; - Point p (relativePosition); - - do - { - if (c->flags.hasHeavyweightPeerFlag) - return c->getPeer()->relativePositionToGlobal (p); - - p += c->getPosition(); - c = c->parentComponent_; - } - while (c != 0); - - return p; + return relativePositionToOtherComponent (0, relativePosition); } const Point Component::globalPositionToRelative (const Point& screenPosition) const @@ -823,29 +808,26 @@ const Point Component::globalPositionToRelative (const Point& screenPo const Point Component::relativePositionToOtherComponent (const Component* const targetComponent, const Point& positionRelativeToThis) const { Point p (positionRelativeToThis); + const Component* c = this; - if (targetComponent != 0) + do { - const Component* c = this; + if (c == targetComponent) + return p; - do + if (c->flags.hasHeavyweightPeerFlag) { - if (c == targetComponent) - return p; - - if (c->flags.hasHeavyweightPeerFlag) - { - p = c->getPeer()->relativePositionToGlobal (p); - break; - } - - p += c->getPosition(); - c = c->parentComponent_; + p = c->getPeer()->relativePositionToGlobal (p); + break; } - while (c != 0); - p = targetComponent->globalPositionToRelative (p); + p += c->getPosition(); + c = c->parentComponent_; } + while (c != 0); + + if (targetComponent != 0) + p = targetComponent->globalPositionToRelative (p); return p; } @@ -1365,9 +1347,12 @@ void Component::internalHierarchyChanged() } //============================================================================== -void* Component::runModalLoopCallback (void* userData) +namespace ComponentHelpers { - return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); + void* runModalLoopCallback (void* userData) + { + return (void*) (pointer_sized_int) static_cast (userData)->runModalLoop(); + } } int Component::runModalLoop() @@ -1376,7 +1361,7 @@ int Component::runModalLoop() { // use a callback so this can be called from non-gui threads return (int) (pointer_sized_int) MessageManager::getInstance() - ->callFunctionOnMessageThread (&runModalLoopCallback, this); + ->callFunctionOnMessageThread (&ComponentHelpers::runModalLoopCallback, this); } if (! isCurrentlyModal()) @@ -1415,7 +1400,7 @@ void Component::exitModalState (const int returnValue) ModalComponentManager::getInstance()->endModal (this, returnValue); flags.currentlyModalFlag = false; - bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } else { @@ -1467,34 +1452,6 @@ Component* JUCE_CALLTYPE Component::getCurrentlyModalComponent (int index) throw return ModalComponentManager::getInstance()->getModalComponent (index); } -void Component::bringModalComponentToFront() -{ - ComponentPeer* lastOne = 0; - - for (int i = 0; i < getNumCurrentlyModalComponents(); ++i) - { - Component* const c = getCurrentlyModalComponent (i); - - if (c == 0) - break; - - ComponentPeer* peer = c->getPeer(); - - if (peer != 0 && peer != lastOne) - { - if (lastOne == 0) - { - peer->toFront (true); - peer->grabFocus(); - } - else - peer->toBehind (lastOne); - - lastOne = peer; - } - } -} - //============================================================================== void Component::setBroughtToFrontOnMouseClick (const bool shouldBeBroughtToFront) throw() { @@ -2084,7 +2041,7 @@ void Component::removeComponentListener (ComponentListener* const listenerToRemo //============================================================================== void Component::inputAttemptWhenModal() { - bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); getLookAndFeel().playAlertSound(); } @@ -2538,7 +2495,7 @@ void Component::internalBroughtToFront() Component* const cm = getCurrentlyModalComponent(); if (cm != 0 && cm->getTopLevelComponent() != getTopLevelComponent()) - bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } void Component::focusGained (FocusChangeType) diff --git a/src/gui/components/juce_Component.h b/src/gui/components/juce_Component.h index 4414d71a81..ff21f97953 100644 --- a/src/gui/components/juce_Component.h +++ b/src/gui/components/juce_Component.h @@ -333,20 +333,19 @@ public: const Rectangle getScreenBounds() const; /** Converts a position relative to this component's top-left into a screen co-ordinate. - @see globalPositionToRelative, relativePositionToOtherComponent */ const Point relativePositionToGlobal (const Point& relativePosition) const; /** Converts a screen co-ordinate into a position relative to this component's top-left. - @see relativePositionToGlobal, relativePositionToOtherComponent */ const Point globalPositionToRelative (const Point& screenPosition) const; /** Converts a position relative to this component's top-left into a position relative to another component's top-left. - + If the targetComponent parameter is null, the coordinate is converted to global screen + coordinates. @see relativePositionToGlobal, globalPositionToRelative */ const Point relativePositionToOtherComponent (const Component* targetComponent, @@ -1213,7 +1212,8 @@ public: if you want to force the system to check that the cursor being displayed is up-to-date (even if the mouse is just sitting there), call this method. - This isn't needed if you're only using setMouseCursor(). + (If you're changing the cursor using setMouseCursor(), you don't need to bother + calling this). */ void updateMouseCursor() const; @@ -2068,8 +2068,6 @@ private: void grabFocusInternal (const FocusChangeType cause, bool canTryParent = true); static void giveAwayFocus(); void sendEnablementChangeMessage(); - static void* runModalLoopCallback (void*); - static void bringModalComponentToFront(); void subtractObscuredRegions (RectangleList& result, const Point& delta, const Rectangle& clipRect, const Component* const compToAvoid) const; void clipObscuredRegions (Graphics& g, const Rectangle& clipRect, int deltaX, int deltaY) const; diff --git a/src/gui/components/juce_ModalComponentManager.cpp b/src/gui/components/juce_ModalComponentManager.cpp index ce525f9373..25b6b4699a 100644 --- a/src/gui/components/juce_ModalComponentManager.cpp +++ b/src/gui/components/juce_ModalComponentManager.cpp @@ -29,6 +29,7 @@ BEGIN_JUCE_NAMESPACE #include "juce_Component.h" #include "juce_ModalComponentManager.h" +#include "windows/juce_ComponentPeer.h" #include "../../events/juce_MessageManager.h" #include "../../application/juce_Application.h" @@ -211,6 +212,34 @@ void ModalComponentManager::handleAsyncUpdate() } } +void ModalComponentManager::bringModalComponentsToFront() +{ + ComponentPeer* lastOne = 0; + + for (int i = 0; i < getNumModalComponents(); ++i) + { + Component* const c = getModalComponent (i); + + if (c == 0) + break; + + ComponentPeer* peer = c->getPeer(); + + if (peer != 0 && peer != lastOne) + { + if (lastOne == 0) + { + peer->toFront (true); + peer->grabFocus(); + } + else + peer->toBehind (lastOne); + + lastOne = peer; + } + } +} + class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback { public: diff --git a/src/gui/components/juce_ModalComponentManager.h b/src/gui/components/juce_ModalComponentManager.h index 44754bebcb..c26fffbc0e 100644 --- a/src/gui/components/juce_ModalComponentManager.h +++ b/src/gui/components/juce_ModalComponentManager.h @@ -106,6 +106,9 @@ public: */ void attachCallback (Component* component, Callback* callback); + /** Brings any modal components to the front. */ + void bringModalComponentsToFront(); + /** Runs the event loop until the currently topmost modal component is dismissed, and returns the exit code for that component. */ diff --git a/src/gui/components/layout/juce_TabbedComponent.cpp b/src/gui/components/layout/juce_TabbedComponent.cpp index 219137adc3..13f356c470 100644 --- a/src/gui/components/layout/juce_TabbedComponent.cpp +++ b/src/gui/components/layout/juce_TabbedComponent.cpp @@ -132,7 +132,7 @@ void TabbedComponent::clearTabs() for (int i = contentComponents.size(); --i >= 0;) { - Component::SafePointer& c = contentComponents.getReference (i); + Component::SafePointer& c = *contentComponents.getUnchecked (i); if (c != 0 && (bool) c->getProperties() [deleteComponentId]) c.deleteAndZero(); @@ -147,7 +147,7 @@ void TabbedComponent::addTab (const String& tabName, const bool deleteComponentWhenNotNeeded, const int insertIndex) { - contentComponents.insert (insertIndex, contentComponent); + contentComponents.insert (insertIndex, new Component::SafePointer (contentComponent)); if (contentComponent != 0) contentComponent->getProperties().set (deleteComponentId, deleteComponentWhenNotNeeded); @@ -162,12 +162,12 @@ void TabbedComponent::setTabName (const int tabIndex, const String& newName) void TabbedComponent::removeTab (const int tabIndex) { - if (tabIndex >= 0 && tabIndex < contentComponents.size()) - { - Component::SafePointer& c = contentComponents.getReference (tabIndex); + Component::SafePointer* c = contentComponents [tabIndex]; - if (c != 0 && (bool) c->getProperties() [deleteComponentId]) - c.deleteAndZero(); + if (c != 0) + { + if ((bool) ((*c)->getProperties() [deleteComponentId])) + c->deleteAndZero(); contentComponents.remove (tabIndex); tabs->removeTab (tabIndex); @@ -186,7 +186,8 @@ const StringArray TabbedComponent::getTabNames() const Component* TabbedComponent::getTabContentComponent (const int tabIndex) const throw() { - return contentComponents [tabIndex]; + Component::SafePointer* const c = contentComponents [tabIndex]; + return c != 0 ? *c : 0; } const Colour TabbedComponent::getTabBackgroundColour (const int tabIndex) const throw() @@ -297,15 +298,15 @@ void TabbedComponent::resized() const Rectangle bounds (indents.subtractedFrom (getLocalBounds())); for (int i = contentComponents.size(); --i >= 0;) - if (contentComponents.getReference (i) != 0) - contentComponents.getReference (i)->setBounds (bounds); + if (*contentComponents.getUnchecked (i) != 0) + (*contentComponents.getUnchecked (i))->setBounds (bounds); } void TabbedComponent::lookAndFeelChanged() { for (int i = contentComponents.size(); --i >= 0;) - if (contentComponents.getReference (i) != 0) - contentComponents.getReference (i)->lookAndFeelChanged(); + if (*contentComponents.getUnchecked (i) != 0) + (*contentComponents.getUnchecked (i))->lookAndFeelChanged(); } void TabbedComponent::changeCallback (const int newCurrentTabIndex, @@ -320,7 +321,7 @@ void TabbedComponent::changeCallback (const int newCurrentTabIndex, if (getCurrentTabIndex() >= 0) { - panelComponent = contentComponents [getCurrentTabIndex()]; + panelComponent = getTabContentComponent (getCurrentTabIndex()); if (panelComponent != 0) { diff --git a/src/gui/components/layout/juce_TabbedComponent.h b/src/gui/components/layout/juce_TabbedComponent.h index 1b979aa768..f6ac5c504d 100644 --- a/src/gui/components/layout/juce_TabbedComponent.h +++ b/src/gui/components/layout/juce_TabbedComponent.h @@ -228,7 +228,7 @@ protected: private: //============================================================================== - Array > contentComponents; + OwnedArray > contentComponents; Component::SafePointer panelComponent; int tabDepth; int outlineThickness, edgeIndent; diff --git a/src/gui/components/lookandfeel/juce_LookAndFeel.cpp b/src/gui/components/lookandfeel/juce_LookAndFeel.cpp index 3fbe7948d5..7f0eb15e53 100644 --- a/src/gui/components/lookandfeel/juce_LookAndFeel.cpp +++ b/src/gui/components/lookandfeel/juce_LookAndFeel.cpp @@ -296,14 +296,15 @@ LookAndFeel::LookAndFeel() for (int i = 0; i < numElementsInArray (standardColours); i += 2) setColour (standardColours [i], Colour (standardColours [i + 1])); - static String defaultSansName, defaultSerifName, defaultFixedName; + static String defaultSansName, defaultSerifName, defaultFixedName, defaultFallback; if (defaultSansName.isEmpty()) - Font::getPlatformDefaultFontNames (defaultSansName, defaultSerifName, defaultFixedName); + Font::getPlatformDefaultFontNames (defaultSansName, defaultSerifName, defaultFixedName, defaultFallback); defaultSans = defaultSansName; defaultSerif = defaultSerifName; defaultFixed = defaultFixedName; + Font::setFallbackFontName (defaultFallback); } LookAndFeel::~LookAndFeel() diff --git a/src/gui/components/menus/juce_PopupMenu.cpp b/src/gui/components/menus/juce_PopupMenu.cpp index e697d63b2e..db54b05af9 100644 --- a/src/gui/components/menus/juce_PopupMenu.cpp +++ b/src/gui/components/menus/juce_PopupMenu.cpp @@ -246,10 +246,10 @@ private: //============================================================================== namespace PopupMenuSettings { - static const int scrollZone = 24; - static const int borderSize = 2; - static const int timerInterval = 50; - static const int dismissCommandId = 0x6287345f; + const int scrollZone = 24; + const int borderSize = 2; + const int timerInterval = 50; + const int dismissCommandId = 0x6287345f; } //============================================================================== @@ -390,9 +390,8 @@ public: bool isScrollZoneActive (bool bottomOne) const { return isScrolling() - && (bottomOne - ? childYOffset < contentHeight - windowPos.getHeight() - : childYOffset > 0); + && (bottomOne ? childYOffset < contentHeight - windowPos.getHeight() + : childYOffset > 0); } //============================================================================== @@ -453,25 +452,10 @@ public: } //============================================================================== - void mouseMove (const MouseEvent&) - { - timerCallback(); - } - - void mouseDown (const MouseEvent&) - { - timerCallback(); - } - - void mouseDrag (const MouseEvent&) - { - timerCallback(); - } - - void mouseUp (const MouseEvent&) - { - timerCallback(); - } + void mouseMove (const MouseEvent&) { timerCallback(); } + void mouseDown (const MouseEvent&) { timerCallback(); } + void mouseDrag (const MouseEvent&) { timerCallback(); } + void mouseUp (const MouseEvent&) { timerCallback(); } void mouseWheelMove (const MouseEvent&, float /*amountX*/, float amountY) { @@ -1151,12 +1135,9 @@ private: } Path areaTowardsSubMenu; - areaTowardsSubMenu.addTriangle ((float) lastMouse.getX(), - (float) lastMouse.getY(), - subX, - (float) activeSubMenu->getScreenY(), - subX, - (float) (activeSubMenu->getScreenY() + activeSubMenu->getHeight())); + areaTowardsSubMenu.addTriangle ((float) lastMouse.getX(), (float) lastMouse.getY(), + subX, (float) activeSubMenu->getScreenY(), + subX, (float) (activeSubMenu->getScreenY() + activeSubMenu->getHeight())); isMovingTowardsMenu = areaTowardsSubMenu.contains ((float) globalMousePos.getX(), (float) globalMousePos.getY()); } @@ -1288,11 +1269,8 @@ void PopupMenu::addSeparatorIfPending() } } -void PopupMenu::addItem (const int itemResultId, - const String& itemText, - const bool isActive, - const bool isTicked, - const Image& iconToUse) +void PopupMenu::addItem (const int itemResultId, const String& itemText, + const bool isActive, const bool isTicked, const Image& iconToUse) { jassert (itemResultId != 0); // 0 is used as a return value to indicate that the user // didn't pick anything, so you shouldn't use it as the id @@ -1366,12 +1344,10 @@ void PopupMenu::addCustomItem (const int itemResultId, class NormalComponentWrapper : public PopupMenuCustomComponent { public: - NormalComponentWrapper (Component* const comp, - const int w, const int h, + NormalComponentWrapper (Component* const comp, const int w, const int h, const bool triggerMenuItemAutomaticallyWhenClicked) : PopupMenuCustomComponent (triggerMenuItemAutomaticallyWhenClicked), - width (w), - height (h) + width (w), height (h) { addAndMakeVisible (comp); } @@ -1405,8 +1381,7 @@ void PopupMenu::addCustomItem (const int itemResultId, const bool triggerMenuItemAutomaticallyWhenClicked) { addCustomItem (itemResultId, - new NormalComponentWrapper (customComponent, - idealWidth, idealHeight, + new NormalComponentWrapper (customComponent, idealWidth, idealHeight, triggerMenuItemAutomaticallyWhenClicked)); } @@ -1481,8 +1456,6 @@ public: { } - ~PopupMenuCompletionCallback() {} - void modalStateFinished (int result) { if (managerOfChosenCommand != 0 && result != 0) @@ -1558,8 +1531,7 @@ int PopupMenu::showMenu (const Rectangle& target, } int PopupMenu::show (const int itemIdThatMustBeVisible, - const int minimumWidth, - const int maximumNumColumns, + const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, ModalComponentManager::Callback* callback) { @@ -1567,31 +1539,24 @@ int PopupMenu::show (const int itemIdThatMustBeVisible, return showAt (mousePos.getX(), mousePos.getY(), itemIdThatMustBeVisible, - minimumWidth, - maximumNumColumns, - standardItemHeight, - callback); + minimumWidth, maximumNumColumns, + standardItemHeight, callback); } -int PopupMenu::showAt (const int screenX, - const int screenY, +int PopupMenu::showAt (const int screenX, const int screenY, const int itemIdThatMustBeVisible, - const int minimumWidth, - const int maximumNumColumns, + const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, ModalComponentManager::Callback* callback) { return showMenu (Rectangle (screenX, screenY, 1, 1), - itemIdThatMustBeVisible, - minimumWidth, maximumNumColumns, - standardItemHeight, - false, 0, callback); + itemIdThatMustBeVisible, minimumWidth, maximumNumColumns, + standardItemHeight, false, 0, callback); } int PopupMenu::showAt (Component* componentToAttachTo, const int itemIdThatMustBeVisible, - const int minimumWidth, - const int maximumNumColumns, + const int minimumWidth, const int maximumNumColumns, const int standardItemHeight, ModalComponentManager::Callback* callback) { @@ -1607,10 +1572,8 @@ int PopupMenu::showAt (Component* componentToAttachTo, else { return show (itemIdThatMustBeVisible, - minimumWidth, - maximumNumColumns, - standardItemHeight, - callback); + minimumWidth, maximumNumColumns, + standardItemHeight, callback); } } diff --git a/src/gui/components/windows/juce_ComponentPeer.cpp b/src/gui/components/windows/juce_ComponentPeer.cpp index 9e72871cb0..9309239665 100644 --- a/src/gui/components/windows/juce_ComponentPeer.cpp +++ b/src/gui/components/windows/juce_ComponentPeer.cpp @@ -353,7 +353,7 @@ void ComponentPeer::handleFocusGain() if (! component->isCurrentlyBlockedByAnotherModalComponent()) component->grabKeyboardFocus(); else - Component::bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } } @@ -507,16 +507,9 @@ void ComponentPeer::handleFileDragDrop (const StringArray& files, const PointuserTriedToCloseWindow(); } -//============================================================================== -void ComponentPeer::bringModalComponentToFront() -{ - Component::bringModalComponentToFront(); -} - //============================================================================== void ComponentPeer::clearMaskedRegion() { diff --git a/src/gui/components/windows/juce_ComponentPeer.h b/src/gui/components/windows/juce_ComponentPeer.h index 3b693a64df..dc5d588a41 100644 --- a/src/gui/components/windows/juce_ComponentPeer.h +++ b/src/gui/components/windows/juce_ComponentPeer.h @@ -344,8 +344,6 @@ public: */ static bool isValidPeer (const ComponentPeer* peer) throw(); - //============================================================================== - static void bringModalComponentToFront(); //============================================================================== virtual const StringArray getAvailableRenderingEngines(); diff --git a/src/gui/components/windows/juce_DialogWindow.cpp b/src/gui/components/windows/juce_DialogWindow.cpp index 4d12ab0b88..c032e14782 100644 --- a/src/gui/components/windows/juce_DialogWindow.cpp +++ b/src/gui/components/windows/juce_DialogWindow.cpp @@ -72,10 +72,6 @@ public: setAlwaysOnTop (true); // for a plugin, make it always-on-top because the host windows are often top-level } - ~TempDialogWindow() - { - } - void closeButtonPressed() { setVisible (false); @@ -84,7 +80,6 @@ public: private: TempDialogWindow (const TempDialogWindow&); TempDialogWindow& operator= (const TempDialogWindow&); - }; int DialogWindow::showModalDialog (const String& dialogTitle, diff --git a/src/gui/graphics/fonts/juce_Font.cpp b/src/gui/graphics/fonts/juce_Font.cpp index 913539f48f..9a0ffdcdb5 100644 --- a/src/gui/graphics/fonts/juce_Font.cpp +++ b/src/gui/graphics/fonts/juce_Font.cpp @@ -38,14 +38,13 @@ BEGIN_JUCE_NAMESPACE //============================================================================== namespace FontValues { - static float limitFontHeight (const float height) throw() + float limitFontHeight (const float height) throw() { return jlimit (0.1f, 10000.0f, height); } - static const float defaultFontHeight = 14.0f; - - static String fallbackFont; + const float defaultFontHeight = 14.0f; + String fallbackFont; } //============================================================================== diff --git a/src/gui/graphics/fonts/juce_Font.h b/src/gui/graphics/fonts/juce_Font.h index 7c4787728f..482d7374d5 100644 --- a/src/gui/graphics/fonts/juce_Font.h +++ b/src/gui/graphics/fonts/juce_Font.h @@ -166,7 +166,7 @@ public: static const String getDefaultMonospacedFontName(); /** Returns the typeface names of the default fonts on the current platform. */ - static void getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed); + static void getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback); //============================================================================== /** Returns the total height of this font. diff --git a/src/gui/graphics/fonts/juce_Typeface.cpp b/src/gui/graphics/fonts/juce_Typeface.cpp index 0fa9345157..565efb6066 100644 --- a/src/gui/graphics/fonts/juce_Typeface.cpp +++ b/src/gui/graphics/fonts/juce_Typeface.cpp @@ -36,7 +36,7 @@ BEGIN_JUCE_NAMESPACE //============================================================================== Typeface::Typeface (const String& name_) throw() - : name (name_) + : name (name_), isFallbackFont (false) { } @@ -44,6 +44,14 @@ Typeface::~Typeface() { } +const Typeface::Ptr Typeface::getFallbackTypeface() +{ + const Font fallbackFont (Font::getFallbackFontName(), 10, 0); + Typeface* t = fallbackFont.getTypeface(); + t->isFallbackFont = true; + return t; +} + //============================================================================== class CustomTypeface::GlyphInfo { @@ -220,7 +228,9 @@ CustomTypeface::GlyphInfo* CustomTypeface::findGlyphSubstituting (const juce_wch Typeface* const fallbackTypeface = fallbackFont.getTypeface(); if (fallbackTypeface != 0 && fallbackTypeface != this) { - //xxx + Path path; + fallbackTypeface->getOutlineForGlyph (character, path); + addGlyph (character, path, fallbackTypeface->getStringWidth (String::charToString (character))); } if (glyph == 0) @@ -334,6 +344,14 @@ float CustomTypeface::getStringWidth (const String& text) { const GlyphInfo* const glyph = findGlyphSubstituting (*t++); + if (glyph == 0 && ! isFallbackFont) + { + const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); + + if (fallbackTypeface != 0) + x += fallbackTypeface->getStringWidth (String::charToString (*t)); + } + if (glyph != 0) x += glyph->getHorizontalSpacing (*t); } @@ -350,7 +368,26 @@ void CustomTypeface::getGlyphPositions (const String& text, Array & resultG while (*t != 0) { const juce_wchar c = *t++; - const GlyphInfo* const glyph = findGlyphSubstituting (c); + const GlyphInfo* const glyph = findGlyph (c, true); + + if (glyph == 0 && ! isFallbackFont) + { + const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); + + if (fallbackTypeface != 0) + { + Array subGlyphs; + Array subOffsets; + fallbackTypeface->getGlyphPositions (String::charToString (c), subGlyphs, subOffsets); + + if (subGlyphs.size() > 0) + { + resultGlyphs.add (subGlyphs.getFirst()); + x += subOffsets[1]; + xOffsets.add (x); + } + } + } if (glyph != 0) { @@ -363,7 +400,16 @@ void CustomTypeface::getGlyphPositions (const String& text, Array & resultG bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path) { - const GlyphInfo* const glyph = findGlyphSubstituting ((juce_wchar) glyphNumber); + const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true); + + if (glyph == 0 && ! isFallbackFont) + { + const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); + + if (fallbackTypeface != 0) + fallbackTypeface->getOutlineForGlyph (glyphNumber, path); + } + if (glyph != 0) { path = glyph->path; diff --git a/src/gui/graphics/fonts/juce_Typeface.h b/src/gui/graphics/fonts/juce_Typeface.h index 8f28014fa1..64b7f474e6 100644 --- a/src/gui/graphics/fonts/juce_Typeface.h +++ b/src/gui/graphics/fonts/juce_Typeface.h @@ -111,9 +111,12 @@ public: protected: String name; + bool isFallbackFont; explicit Typeface (const String& name) throw(); + static const Ptr getFallbackTypeface(); + private: Typeface (const Typeface&); Typeface& operator= (const Typeface&); diff --git a/src/native/linux/juce_linux_Fonts.cpp b/src/native/linux/juce_linux_Fonts.cpp index 29e28d83d4..dd4d5c40ff 100644 --- a/src/native/linux/juce_linux_Fonts.cpp +++ b/src/native/linux/juce_linux_Fonts.cpp @@ -1,572 +1,572 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-10 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -// (This file gets included by juce_linux_NativeCode.cpp, rather than being -// compiled on its own). -#if JUCE_INCLUDED_FILE - - -//============================================================================== -class FreeTypeFontFace -{ -public: - //============================================================================== - enum FontStyle - { - Plain = 0, - Bold = 1, - Italic = 2 - }; - - //============================================================================== - FreeTypeFontFace (const String& familyName) - : hasSerif (false), - monospaced (false) - { - family = familyName; - } - - void setFileName (const String& name, const int faceIndex, FontStyle style) - { - if (names [(int) style].fileName.isEmpty()) - { - names [(int) style].fileName = name; - names [(int) style].faceIndex = faceIndex; - } - } - - const String& getFamilyName() const throw() { return family; } - - const String& getFileName (const int style, int& faceIndex) const throw() - { - faceIndex = names[style].faceIndex; - return names[style].fileName; - } - - void setMonospaced (bool mono) throw() { monospaced = mono; } - bool getMonospaced() const throw() { return monospaced; } - - void setSerif (const bool serif) throw() { hasSerif = serif; } - bool getSerif() const throw() { return hasSerif; } - -private: - //============================================================================== - String family; - - struct FontNameIndex - { - String fileName; - int faceIndex; - }; - - FontNameIndex names[4]; - bool hasSerif, monospaced; -}; - -//============================================================================== -class FreeTypeInterface : public DeletedAtShutdown -{ -public: - //============================================================================== - FreeTypeInterface() - : ftLib (0), - lastFace (0), - lastBold (false), - lastItalic (false) - { - if (FT_Init_FreeType (&ftLib) != 0) - { - ftLib = 0; - DBG ("Failed to initialize FreeType"); - } - - StringArray fontDirs; - fontDirs.addTokens (String::fromUTF8 (getenv ("JUCE_FONT_PATH")), ";,", String::empty); - fontDirs.removeEmptyStrings (true); - - if (fontDirs.size() == 0) - { - const ScopedPointer fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf"))); - - if (fontsInfo != 0) - { - forEachXmlChildElementWithTagName (*fontsInfo, e, "dir") - { - fontDirs.add (e->getAllSubText().trim()); - } - } - } - - if (fontDirs.size() == 0) - fontDirs.add ("/usr/X11R6/lib/X11/fonts"); - - for (int i = 0; i < fontDirs.size(); ++i) - enumerateFaces (fontDirs[i]); - } - - ~FreeTypeInterface() - { - if (lastFace != 0) - FT_Done_Face (lastFace); - - if (ftLib != 0) - FT_Done_FreeType (ftLib); - - clearSingletonInstance(); - } - - //============================================================================== - FreeTypeFontFace* findOrCreate (const String& familyName, const bool create = false) - { - for (int i = 0; i < faces.size(); i++) - if (faces[i]->getFamilyName() == familyName) - return faces[i]; - - if (! create) - return 0; - - FreeTypeFontFace* newFace = new FreeTypeFontFace (familyName); - faces.add (newFace); - - return newFace; - } - - // Enumerate all font faces available in a given directory - void enumerateFaces (const String& path) - { - File dirPath (path); - if (path.isEmpty() || ! dirPath.isDirectory()) - return; - - DirectoryIterator di (dirPath, true); - - while (di.next()) - { - File possible (di.getFile()); - - if (possible.hasFileExtension ("ttf") - || possible.hasFileExtension ("pfb") - || possible.hasFileExtension ("pcf")) - { - FT_Face face; - int faceIndex = 0; - int numFaces = 0; - - do - { - if (FT_New_Face (ftLib, possible.getFullPathName().toUTF8(), - faceIndex, &face) == 0) - { - if (faceIndex == 0) - numFaces = face->num_faces; - - if ((face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) - { - FreeTypeFontFace* const newFace = findOrCreate (face->family_name, true); - int style = (int) FreeTypeFontFace::Plain; - - if ((face->style_flags & FT_STYLE_FLAG_BOLD) != 0) - style |= (int) FreeTypeFontFace::Bold; - - if ((face->style_flags & FT_STYLE_FLAG_ITALIC) != 0) - style |= (int) FreeTypeFontFace::Italic; - - newFace->setFileName (possible.getFullPathName(), faceIndex, (FreeTypeFontFace::FontStyle) style); - newFace->setMonospaced ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0); - - // Surely there must be a better way to do this? - const String name (face->family_name); - newFace->setSerif (! (name.containsIgnoreCase ("Sans") - || name.containsIgnoreCase ("Verdana") - || name.containsIgnoreCase ("Arial"))); - } - - FT_Done_Face (face); - } - - ++faceIndex; - } - while (faceIndex < numFaces); - } - } - } - - // Create a FreeType face object for a given font - FT_Face createFT_Face (const String& fontName, const bool bold, const bool italic) - { - FT_Face face = 0; - - if (fontName == lastFontName && bold == lastBold && italic == lastItalic) - { - face = lastFace; - } - else - { - if (lastFace != 0) - { - FT_Done_Face (lastFace); - lastFace = 0; - } - - lastFontName = fontName; - lastBold = bold; - lastItalic = italic; - - FreeTypeFontFace* const ftFace = findOrCreate (fontName); - - if (ftFace != 0) - { - int style = (int) FreeTypeFontFace::Plain; - - if (bold) - style |= (int) FreeTypeFontFace::Bold; - - if (italic) - style |= (int) FreeTypeFontFace::Italic; - - int faceIndex; - String fileName (ftFace->getFileName (style, faceIndex)); - - if (fileName.isEmpty()) - { - style ^= (int) FreeTypeFontFace::Bold; - - fileName = ftFace->getFileName (style, faceIndex); - - if (fileName.isEmpty()) - { - style ^= (int) FreeTypeFontFace::Bold; - style ^= (int) FreeTypeFontFace::Italic; - - fileName = ftFace->getFileName (style, faceIndex); - - if (! fileName.length()) - { - style ^= (int) FreeTypeFontFace::Bold; - fileName = ftFace->getFileName (style, faceIndex); - } - } - } - - if (! FT_New_Face (ftLib, fileName.toUTF8(), faceIndex, &lastFace)) - { - face = lastFace; - - // If there isn't a unicode charmap then select the first one. - if (FT_Select_Charmap (face, ft_encoding_unicode)) - FT_Set_Charmap (face, face->charmaps[0]); - } - } - } - - return face; - } - - bool addGlyph (FT_Face face, CustomTypeface& dest, uint32 character) - { - const unsigned int glyphIndex = FT_Get_Char_Index (face, character); - const float height = (float) (face->ascender - face->descender); - const float scaleX = 1.0f / height; - const float scaleY = -1.0f / height; - Path destShape; - - if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) != 0 - || face->glyph->format != ft_glyph_format_outline) - { - return false; - } - - const FT_Outline* const outline = &face->glyph->outline; - const short* const contours = outline->contours; - const char* const tags = outline->tags; - FT_Vector* const points = outline->points; - - for (int c = 0; c < outline->n_contours; c++) - { - const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1; - const int endPoint = contours[c]; - - for (int p = startPoint; p <= endPoint; p++) - { - const float x = scaleX * points[p].x; - const float y = scaleY * points[p].y; - - if (p == startPoint) - { - if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) - { - float x2 = scaleX * points [endPoint].x; - float y2 = scaleY * points [endPoint].y; - - if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On) - { - x2 = (x + x2) * 0.5f; - y2 = (y + y2) * 0.5f; - } - - destShape.startNewSubPath (x2, y2); - } - else - { - destShape.startNewSubPath (x, y); - } - } - - if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On) - { - if (p != startPoint) - destShape.lineTo (x, y); - } - else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) - { - const int nextIndex = (p == endPoint) ? startPoint : p + 1; - float x2 = scaleX * points [nextIndex].x; - float y2 = scaleY * points [nextIndex].y; - - if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic) - { - x2 = (x + x2) * 0.5f; - y2 = (y + y2) * 0.5f; - } - else - { - ++p; - } - - destShape.quadraticTo (x, y, x2, y2); - } - else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic) - { - if (p >= endPoint) - return false; - - const int next1 = p + 1; - const int next2 = (p == (endPoint - 1)) ? startPoint : p + 2; - - const float x2 = scaleX * points [next1].x; - const float y2 = scaleY * points [next1].y; - const float x3 = scaleX * points [next2].x; - const float y3 = scaleY * points [next2].y; - - if (FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic - || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On) - return false; - - destShape.cubicTo (x, y, x2, y2, x3, y3); - p += 2; - } - } - - destShape.closeSubPath(); - } - - dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance / height); - - if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) - addKerning (face, dest, character, glyphIndex); - - return true; - } - - void addKerning (FT_Face face, CustomTypeface& dest, const uint32 character, const uint32 glyphIndex) - { - const float height = (float) (face->ascender - face->descender); - - uint32 rightGlyphIndex; - uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex); - - while (rightGlyphIndex != 0) - { - FT_Vector kerning; - - if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0) - { - if (kerning.x != 0) - dest.addKerningPair (character, rightCharCode, kerning.x / height); - } - - rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex); - } - } - - // Add a glyph to a font - bool addGlyphToFont (const uint32 character, const String& fontName, - bool bold, bool italic, CustomTypeface& dest) - { - FT_Face face = createFT_Face (fontName, bold, italic); - - return face != 0 && addGlyph (face, dest, character); - } - - //============================================================================== - void getFamilyNames (StringArray& familyNames) const - { - for (int i = 0; i < faces.size(); i++) - familyNames.add (faces[i]->getFamilyName()); - } - - void getMonospacedNames (StringArray& monoSpaced) const - { - for (int i = 0; i < faces.size(); i++) - if (faces[i]->getMonospaced()) - monoSpaced.add (faces[i]->getFamilyName()); - } - - void getSerifNames (StringArray& serif) const - { - for (int i = 0; i < faces.size(); i++) - if (faces[i]->getSerif()) - serif.add (faces[i]->getFamilyName()); - } - - void getSansSerifNames (StringArray& sansSerif) const - { - for (int i = 0; i < faces.size(); i++) - if (! faces[i]->getSerif()) - sansSerif.add (faces[i]->getFamilyName()); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (FreeTypeInterface) - -private: - //============================================================================== - FT_Library ftLib; - FT_Face lastFace; - String lastFontName; - bool lastBold, lastItalic; - OwnedArray faces; -}; - -juce_ImplementSingleton_SingleThreaded (FreeTypeInterface) - - -//============================================================================== -class FreetypeTypeface : public CustomTypeface -{ -public: - FreetypeTypeface (const Font& font) - { - FT_Face face = FreeTypeInterface::getInstance() - ->createFT_Face (font.getTypefaceName(), font.isBold(), font.isItalic()); - - if (face == 0) - { -#if JUCE_DEBUG - String msg ("Failed to create typeface: "); - msg << font.getTypefaceName() << " " << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' '); - DBG (msg); -#endif - } - else - { - setCharacteristics (font.getTypefaceName(), - face->ascender / (float) (face->ascender - face->descender), - font.isBold(), font.isItalic(), - L' '); - } - } - - bool loadGlyphIfPossible (juce_wchar character) - { - return FreeTypeInterface::getInstance() - ->addGlyphToFont (character, name, isBold, isItalic, *this); - } -}; - -const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) -{ - return new FreetypeTypeface (font); -} - -//============================================================================== -const StringArray Font::findAllTypefaceNames() -{ - StringArray s; - FreeTypeInterface::getInstance()->getFamilyNames (s); - s.sort (true); - return s; -} - -namespace -{ - const String pickBestFont (const StringArray& names, - const char* const choicesString) - { - StringArray choices; - choices.addTokens (String (choicesString), ",", String::empty); - choices.trim(); - choices.removeEmptyStrings(); - - int i, j; - for (j = 0; j < choices.size(); ++j) - if (names.contains (choices[j], true)) - return choices[j]; - - for (j = 0; j < choices.size(); ++j) - for (i = 0; i < names.size(); i++) - if (names[i].startsWithIgnoreCase (choices[j])) - return names[i]; - - for (j = 0; j < choices.size(); ++j) - for (i = 0; i < names.size(); i++) - if (names[i].containsIgnoreCase (choices[j])) - return names[i]; - - return names[0]; - } - - const String linux_getDefaultSansSerifFontName() - { - StringArray allFonts; - FreeTypeInterface::getInstance()->getSansSerifNames (allFonts); - - return pickBestFont (allFonts, "Verdana, Bitstream Vera Sans, Luxi Sans, Sans"); - } - - const String linux_getDefaultSerifFontName() - { - StringArray allFonts; - FreeTypeInterface::getInstance()->getSerifNames (allFonts); - - return pickBestFont (allFonts, "Bitstream Vera Serif, Times, Nimbus Roman, Serif"); - } - - const String linux_getDefaultMonospacedFontName() - { - StringArray allFonts; - FreeTypeInterface::getInstance()->getMonospacedNames (allFonts); - - return pickBestFont (allFonts, "Bitstream Vera Sans Mono, Courier, Sans Mono, Mono"); - } -} - -void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) -{ - defaultSans = linux_getDefaultSansSerifFontName(); - defaultSerif = linux_getDefaultSerifFontName(); - defaultFixed = linux_getDefaultMonospacedFontName(); -} - -#endif +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +// (This file gets included by juce_linux_NativeCode.cpp, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + + +//============================================================================== +class FreeTypeFontFace +{ +public: + //============================================================================== + enum FontStyle + { + Plain = 0, + Bold = 1, + Italic = 2 + }; + + //============================================================================== + FreeTypeFontFace (const String& familyName) + : hasSerif (false), + monospaced (false) + { + family = familyName; + } + + void setFileName (const String& name, const int faceIndex, FontStyle style) + { + if (names [(int) style].fileName.isEmpty()) + { + names [(int) style].fileName = name; + names [(int) style].faceIndex = faceIndex; + } + } + + const String& getFamilyName() const throw() { return family; } + + const String& getFileName (const int style, int& faceIndex) const throw() + { + faceIndex = names[style].faceIndex; + return names[style].fileName; + } + + void setMonospaced (bool mono) throw() { monospaced = mono; } + bool getMonospaced() const throw() { return monospaced; } + + void setSerif (const bool serif) throw() { hasSerif = serif; } + bool getSerif() const throw() { return hasSerif; } + +private: + //============================================================================== + String family; + + struct FontNameIndex + { + String fileName; + int faceIndex; + }; + + FontNameIndex names[4]; + bool hasSerif, monospaced; +}; + +//============================================================================== +class FreeTypeInterface : public DeletedAtShutdown +{ +public: + //============================================================================== + FreeTypeInterface() + : ftLib (0), + lastFace (0), + lastBold (false), + lastItalic (false) + { + if (FT_Init_FreeType (&ftLib) != 0) + { + ftLib = 0; + DBG ("Failed to initialize FreeType"); + } + + StringArray fontDirs; + fontDirs.addTokens (String::fromUTF8 (getenv ("JUCE_FONT_PATH")), ";,", String::empty); + fontDirs.removeEmptyStrings (true); + + if (fontDirs.size() == 0) + { + const ScopedPointer fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf"))); + + if (fontsInfo != 0) + { + forEachXmlChildElementWithTagName (*fontsInfo, e, "dir") + { + fontDirs.add (e->getAllSubText().trim()); + } + } + } + + if (fontDirs.size() == 0) + fontDirs.add ("/usr/X11R6/lib/X11/fonts"); + + for (int i = 0; i < fontDirs.size(); ++i) + enumerateFaces (fontDirs[i]); + } + + ~FreeTypeInterface() + { + if (lastFace != 0) + FT_Done_Face (lastFace); + + if (ftLib != 0) + FT_Done_FreeType (ftLib); + + clearSingletonInstance(); + } + + //============================================================================== + FreeTypeFontFace* findOrCreate (const String& familyName, const bool create = false) + { + for (int i = 0; i < faces.size(); i++) + if (faces[i]->getFamilyName() == familyName) + return faces[i]; + + if (! create) + return 0; + + FreeTypeFontFace* newFace = new FreeTypeFontFace (familyName); + faces.add (newFace); + + return newFace; + } + + // Enumerate all font faces available in a given directory + void enumerateFaces (const String& path) + { + File dirPath (path); + if (path.isEmpty() || ! dirPath.isDirectory()) + return; + + DirectoryIterator di (dirPath, true); + + while (di.next()) + { + File possible (di.getFile()); + + if (possible.hasFileExtension ("ttf") + || possible.hasFileExtension ("pfb") + || possible.hasFileExtension ("pcf")) + { + FT_Face face; + int faceIndex = 0; + int numFaces = 0; + + do + { + if (FT_New_Face (ftLib, possible.getFullPathName().toUTF8(), + faceIndex, &face) == 0) + { + if (faceIndex == 0) + numFaces = face->num_faces; + + if ((face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) + { + FreeTypeFontFace* const newFace = findOrCreate (face->family_name, true); + int style = (int) FreeTypeFontFace::Plain; + + if ((face->style_flags & FT_STYLE_FLAG_BOLD) != 0) + style |= (int) FreeTypeFontFace::Bold; + + if ((face->style_flags & FT_STYLE_FLAG_ITALIC) != 0) + style |= (int) FreeTypeFontFace::Italic; + + newFace->setFileName (possible.getFullPathName(), faceIndex, (FreeTypeFontFace::FontStyle) style); + newFace->setMonospaced ((face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0); + + // Surely there must be a better way to do this? + const String name (face->family_name); + newFace->setSerif (! (name.containsIgnoreCase ("Sans") + || name.containsIgnoreCase ("Verdana") + || name.containsIgnoreCase ("Arial"))); + } + + FT_Done_Face (face); + } + + ++faceIndex; + } + while (faceIndex < numFaces); + } + } + } + + // Create a FreeType face object for a given font + FT_Face createFT_Face (const String& fontName, const bool bold, const bool italic) + { + FT_Face face = 0; + + if (fontName == lastFontName && bold == lastBold && italic == lastItalic) + { + face = lastFace; + } + else + { + if (lastFace != 0) + { + FT_Done_Face (lastFace); + lastFace = 0; + } + + lastFontName = fontName; + lastBold = bold; + lastItalic = italic; + + FreeTypeFontFace* const ftFace = findOrCreate (fontName); + + if (ftFace != 0) + { + int style = (int) FreeTypeFontFace::Plain; + + if (bold) + style |= (int) FreeTypeFontFace::Bold; + + if (italic) + style |= (int) FreeTypeFontFace::Italic; + + int faceIndex; + String fileName (ftFace->getFileName (style, faceIndex)); + + if (fileName.isEmpty()) + { + style ^= (int) FreeTypeFontFace::Bold; + + fileName = ftFace->getFileName (style, faceIndex); + + if (fileName.isEmpty()) + { + style ^= (int) FreeTypeFontFace::Bold; + style ^= (int) FreeTypeFontFace::Italic; + + fileName = ftFace->getFileName (style, faceIndex); + + if (! fileName.length()) + { + style ^= (int) FreeTypeFontFace::Bold; + fileName = ftFace->getFileName (style, faceIndex); + } + } + } + + if (! FT_New_Face (ftLib, fileName.toUTF8(), faceIndex, &lastFace)) + { + face = lastFace; + + // If there isn't a unicode charmap then select the first one. + if (FT_Select_Charmap (face, ft_encoding_unicode)) + FT_Set_Charmap (face, face->charmaps[0]); + } + } + } + + return face; + } + + bool addGlyph (FT_Face face, CustomTypeface& dest, uint32 character) + { + const unsigned int glyphIndex = FT_Get_Char_Index (face, character); + const float height = (float) (face->ascender - face->descender); + const float scaleX = 1.0f / height; + const float scaleY = -1.0f / height; + Path destShape; + + if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) != 0 + || face->glyph->format != ft_glyph_format_outline) + { + return false; + } + + const FT_Outline* const outline = &face->glyph->outline; + const short* const contours = outline->contours; + const char* const tags = outline->tags; + FT_Vector* const points = outline->points; + + for (int c = 0; c < outline->n_contours; c++) + { + const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1; + const int endPoint = contours[c]; + + for (int p = startPoint; p <= endPoint; p++) + { + const float x = scaleX * points[p].x; + const float y = scaleY * points[p].y; + + if (p == startPoint) + { + if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) + { + float x2 = scaleX * points [endPoint].x; + float y2 = scaleY * points [endPoint].y; + + if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On) + { + x2 = (x + x2) * 0.5f; + y2 = (y + y2) * 0.5f; + } + + destShape.startNewSubPath (x2, y2); + } + else + { + destShape.startNewSubPath (x, y); + } + } + + if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On) + { + if (p != startPoint) + destShape.lineTo (x, y); + } + else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) + { + const int nextIndex = (p == endPoint) ? startPoint : p + 1; + float x2 = scaleX * points [nextIndex].x; + float y2 = scaleY * points [nextIndex].y; + + if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic) + { + x2 = (x + x2) * 0.5f; + y2 = (y + y2) * 0.5f; + } + else + { + ++p; + } + + destShape.quadraticTo (x, y, x2, y2); + } + else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic) + { + if (p >= endPoint) + return false; + + const int next1 = p + 1; + const int next2 = (p == (endPoint - 1)) ? startPoint : p + 2; + + const float x2 = scaleX * points [next1].x; + const float y2 = scaleY * points [next1].y; + const float x3 = scaleX * points [next2].x; + const float y3 = scaleY * points [next2].y; + + if (FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic + || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On) + return false; + + destShape.cubicTo (x, y, x2, y2, x3, y3); + p += 2; + } + } + + destShape.closeSubPath(); + } + + dest.addGlyph (character, destShape, face->glyph->metrics.horiAdvance / height); + + if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) + addKerning (face, dest, character, glyphIndex); + + return true; + } + + void addKerning (FT_Face face, CustomTypeface& dest, const uint32 character, const uint32 glyphIndex) + { + const float height = (float) (face->ascender - face->descender); + + uint32 rightGlyphIndex; + uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex); + + while (rightGlyphIndex != 0) + { + FT_Vector kerning; + + if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0) + { + if (kerning.x != 0) + dest.addKerningPair (character, rightCharCode, kerning.x / height); + } + + rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex); + } + } + + // Add a glyph to a font + bool addGlyphToFont (const uint32 character, const String& fontName, + bool bold, bool italic, CustomTypeface& dest) + { + FT_Face face = createFT_Face (fontName, bold, italic); + + return face != 0 && addGlyph (face, dest, character); + } + + //============================================================================== + void getFamilyNames (StringArray& familyNames) const + { + for (int i = 0; i < faces.size(); i++) + familyNames.add (faces[i]->getFamilyName()); + } + + void getMonospacedNames (StringArray& monoSpaced) const + { + for (int i = 0; i < faces.size(); i++) + if (faces[i]->getMonospaced()) + monoSpaced.add (faces[i]->getFamilyName()); + } + + void getSerifNames (StringArray& serif) const + { + for (int i = 0; i < faces.size(); i++) + if (faces[i]->getSerif()) + serif.add (faces[i]->getFamilyName()); + } + + void getSansSerifNames (StringArray& sansSerif) const + { + for (int i = 0; i < faces.size(); i++) + if (! faces[i]->getSerif()) + sansSerif.add (faces[i]->getFamilyName()); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (FreeTypeInterface) + +private: + //============================================================================== + FT_Library ftLib; + FT_Face lastFace; + String lastFontName; + bool lastBold, lastItalic; + OwnedArray faces; +}; + +juce_ImplementSingleton_SingleThreaded (FreeTypeInterface) + + +//============================================================================== +class FreetypeTypeface : public CustomTypeface +{ +public: + FreetypeTypeface (const Font& font) + { + FT_Face face = FreeTypeInterface::getInstance() + ->createFT_Face (font.getTypefaceName(), font.isBold(), font.isItalic()); + + if (face == 0) + { +#if JUCE_DEBUG + String msg ("Failed to create typeface: "); + msg << font.getTypefaceName() << " " << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' '); + DBG (msg); +#endif + } + else + { + setCharacteristics (font.getTypefaceName(), + face->ascender / (float) (face->ascender - face->descender), + font.isBold(), font.isItalic(), + L' '); + } + } + + bool loadGlyphIfPossible (juce_wchar character) + { + return FreeTypeInterface::getInstance() + ->addGlyphToFont (character, name, isBold, isItalic, *this); + } +}; + +const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) +{ + return new FreetypeTypeface (font); +} + +//============================================================================== +const StringArray Font::findAllTypefaceNames() +{ + StringArray s; + FreeTypeInterface::getInstance()->getFamilyNames (s); + s.sort (true); + return s; +} + +namespace +{ + const String pickBestFont (const StringArray& names, + const char* const choicesString) + { + StringArray choices; + choices.addTokens (String (choicesString), ",", String::empty); + choices.trim(); + choices.removeEmptyStrings(); + + int i, j; + for (j = 0; j < choices.size(); ++j) + if (names.contains (choices[j], true)) + return choices[j]; + + for (j = 0; j < choices.size(); ++j) + for (i = 0; i < names.size(); i++) + if (names[i].startsWithIgnoreCase (choices[j])) + return names[i]; + + for (j = 0; j < choices.size(); ++j) + for (i = 0; i < names.size(); i++) + if (names[i].containsIgnoreCase (choices[j])) + return names[i]; + + return names[0]; + } + + const String linux_getDefaultSansSerifFontName() + { + StringArray allFonts; + FreeTypeInterface::getInstance()->getSansSerifNames (allFonts); + + return pickBestFont (allFonts, "Verdana, Bitstream Vera Sans, Luxi Sans, Sans"); + } + + const String linux_getDefaultSerifFontName() + { + StringArray allFonts; + FreeTypeInterface::getInstance()->getSerifNames (allFonts); + + return pickBestFont (allFonts, "Bitstream Vera Serif, Times, Nimbus Roman, Serif"); + } + + const String linux_getDefaultMonospacedFontName() + { + StringArray allFonts; + FreeTypeInterface::getInstance()->getMonospacedNames (allFonts); + + return pickBestFont (allFonts, "Bitstream Vera Sans Mono, Courier, Sans Mono, Mono"); + } +} + +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& /*defaultFallback*/) +{ + defaultSans = linux_getDefaultSansSerifFontName(); + defaultSerif = linux_getDefaultSerifFontName(); + defaultFixed = linux_getDefaultMonospacedFontName(); +} + +#endif diff --git a/src/native/mac/juce_iphone_UIViewComponentPeer.mm b/src/native/mac/juce_iphone_UIViewComponentPeer.mm index 38a3bac6d8..3c01588f24 100644 --- a/src/native/mac/juce_iphone_UIViewComponentPeer.mm +++ b/src/native/mac/juce_iphone_UIViewComponentPeer.mm @@ -799,7 +799,7 @@ void juce_HandleProcessFocusChange() { currentlyFocusedPeer->handleFocusGain(); - ComponentPeer::bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } else { diff --git a/src/native/mac/juce_mac_Fonts.mm b/src/native/mac/juce_mac_Fonts.mm index a9ce76abac..f00fd8e0a0 100644 --- a/src/native/mac/juce_mac_Fonts.mm +++ b/src/native/mac/juce_mac_Fonts.mm @@ -508,7 +508,7 @@ const StringArray Font::findAllTypefaceNames() return names; } -void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback) { #if JUCE_IOS defaultSans = "Helvetica"; @@ -519,6 +519,8 @@ void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSeri defaultSerif = "Times New Roman"; defaultFixed = "Monaco"; #endif + + defaultFallback = "Arial Unicode MS"; } #endif diff --git a/src/native/mac/juce_mac_NSViewComponentPeer.mm b/src/native/mac/juce_mac_NSViewComponentPeer.mm index 5feeb69036..fe9139bb34 100644 --- a/src/native/mac/juce_mac_NSViewComponentPeer.mm +++ b/src/native/mac/juce_mac_NSViewComponentPeer.mm @@ -1291,7 +1291,7 @@ void juce_HandleProcessFocusChange() { NSViewComponentPeer::currentlyFocusedPeer->handleFocusGain(); - ComponentPeer::bringModalComponentToFront(); + ModalComponentManager::getInstance()->bringModalComponentsToFront(); } else { diff --git a/src/native/windows/juce_win32_Fonts.cpp b/src/native/windows/juce_win32_Fonts.cpp index 80dccd64f9..4b1b079619 100644 --- a/src/native/windows/juce_win32_Fonts.cpp +++ b/src/native/windows/juce_win32_Fonts.cpp @@ -104,7 +104,7 @@ const StringArray Font::findAllTypefaceNames() extern bool juce_IsRunningInWine(); -void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) +void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback) { if (juce_IsRunningInWine()) { @@ -115,9 +115,10 @@ void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSeri } else { - defaultSans = "Verdana"; - defaultSerif = "Times"; - defaultFixed = "Lucida Console"; + defaultSans = "Verdana"; + defaultSerif = "Times"; + defaultFixed = "Lucida Console"; + defaultFallback = "Tahoma"; // (contains plenty of unicode characters) } } @@ -274,6 +275,10 @@ public: GLYPHMETRICS gm; + // if this is the fallback font, skip checking for the glyph's existence. This is because + // with fonts like Tahoma, GetGlyphIndices can say that a glyph doesn't exist, but it still + // gets correctly created later on. + if (! isFallbackFont) { const WCHAR charToTest[] = { (WCHAR) character, 0 }; WORD index = 0;