void paint (Graphics& g) override
    {
        jassert (dynamic_cast <CodeEditorComponent*> (getParentComponent()) != nullptr);
        const CodeEditorComponent& editor = *static_cast <CodeEditorComponent*> (getParentComponent());

        g.fillAll (editor.findColour (CodeEditorComponent::backgroundColourId)
                    .overlaidWith (editor.findColour (lineNumberBackgroundId)));

        const Rectangle<int> clip (g.getClipBounds());
        const int lineH = editor.lineHeight;
        const float lineHeightFloat = (float) lineH;
        const int firstLineToDraw = jmax (0, clip.getY() / lineH);
        const int lastLineToDraw = jmin (editor.lines.size(), clip.getBottom() / lineH + 1,
                                         lastNumLines - editor.firstLineOnScreen);

        const Font lineNumberFont (editor.getFont().withHeight (jmin (13.0f, lineHeightFloat * 0.8f)));
        const float w = getWidth() - 2.0f;

        GlyphArrangement ga;
        for (int i = firstLineToDraw; i < lastLineToDraw; ++i)
            ga.addFittedText (lineNumberFont, String (editor.firstLineOnScreen + i + 1),
                              0, (float) (lineH * i), w, lineHeightFloat,
                              Justification::centredRight, 1, 0.2f);

        g.setColour (editor.findColour (lineNumberTextId));
        ga.draw (g);
    }
예제 #2
0
    static float getAverageY (const Font& font, const char* chars, bool getTop)
    {
        GlyphArrangement ga;
        ga.addLineOfText (font, chars, 0, 0);

        Array<float> y;
        DefaultElementComparator<float> sorter;

        for (int i = 0; i < ga.getNumGlyphs(); ++i)
        {
            Path p;
            ga.getGlyph (i).createPath (p);
            Rectangle<float> bounds (p.getBounds());

            if (! p.isEmpty())
                y.addSorted (sorter, getTop ? bounds.getY() : bounds.getBottom());
        }

        float median = y[y.size() / 2];

        float total = 0;
        int num = 0;

        for (int i = 0; i < y.size(); ++i)
        {
            if (std::abs (median - y.getUnchecked(i)) < 0.05f * (float) standardHeight)
            {
                total += y.getUnchecked(i);
                ++num;
            }
        }

        return num < 4 ? 0.0f : total / (num * (float) standardHeight);
    }
//==============================================================================
void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY,
                                   const Justification& justification) const
{
    if (text.isNotEmpty()
         && startX < context.getClipBounds().getRight())
    {
        GlyphArrangement arr;
        arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY);

        // Don't pass any vertical placement flags to this method - they'll be ignored.
        jassert (justification.getOnlyVerticalFlags() == 0);

        const int flags = justification.getOnlyHorizontalFlags();

        if (flags != Justification::left)
        {
            float w = arr.getBoundingBox (0, -1, true).getWidth();

            if ((flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0)
                w /= 2.0f;

            arr.draw (*this, AffineTransform::translation (-w, 0));
        }
        else
        {
            arr.draw (*this);
        }
    }
}
void Graphics::drawTextAsPath (const String& text, const AffineTransform& transform) const
{
    if (text.isNotEmpty())
    {
        GlyphArrangement arr;
        arr.addLineOfText (context.getFont(), text, 0.0f, 0.0f);
        arr.draw (*this, transform);
    }
}
예제 #5
0
void CtrlrTabsLF::drawTabButtonText (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown)
{
    const Rectangle<float> area (button.getTextArea().toFloat());

    float length = area.getWidth();
    float depth  = area.getHeight();

    if (button.getTabbedButtonBar().isVertical())
        std::swap (length, depth);

    Font otherTabFont  = owner.getOwner().getOwner().getOwner().getFontManager().getFontFromString (owner.getProperty(Ids::uiTabsTabFont));
    Font activeTabFont = owner.getOwner().getOwner().getOwner().getFontManager().getFontFromString (owner.getProperty(Ids::uiTabsFrontTabFont));

    otherTabFont.setUnderline (button.hasKeyboardFocus (false));
    activeTabFont.setUnderline (button.hasKeyboardFocus (false));

    GlyphArrangement textLayout;
    textLayout.addFittedText (button.isFrontTab() ? activeTabFont : otherTabFont, button.getButtonText().trim(),
                              0.0f, 0.0f, (float) length, (float) depth,
                              Justification::centred,
                              jmax<int> (1, depth / 12));
    AffineTransform t;

    switch (button.getTabbedButtonBar().getOrientation())
    {
    case TabbedButtonBar::TabsAtLeft:
        t = t.rotated (float_Pi * -0.5f).translated (area.getX(), area.getBottom());
        break;
    case TabbedButtonBar::TabsAtRight:
        t = t.rotated (float_Pi *  0.5f).translated (area.getRight(), area.getY());
        break;
    case TabbedButtonBar::TabsAtTop:
    case TabbedButtonBar::TabsAtBottom:
        t = t.translated (area.getX(), area.getY());
        break;
    default:
        jassertfalse;
        break;
    }

    Colour col;

    if (button.isFrontTab() && (button.isColourSpecified (TabbedButtonBar::frontTextColourId)
                                || isColourSpecified (TabbedButtonBar::frontTextColourId)))
        col = findColour (TabbedButtonBar::frontTextColourId);
    else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId)
             || isColourSpecified (TabbedButtonBar::tabTextColourId))
        col = findColour (TabbedButtonBar::tabTextColourId);
    else
        col = button.getTabBackgroundColour().contrasting();

    const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f;

    g.setColour (col.withMultipliedAlpha (alpha));
    textLayout.draw (g, t);
}
예제 #6
0
//==============================================================================
void DrawableText::paint (Graphics& g)
{
    transformContextToCorrectOrigin (g);

    g.setColour (colour);

    GlyphArrangement ga;
    const AffineTransform transform (getArrangementAndTransform (ga));
    ga.draw (g, transform);
}
예제 #7
0
//==============================================================================
void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY) const
{
    if (text.isNotEmpty()
         && startX < context->getClipBounds().getRight())
    {
        GlyphArrangement arr;
        arr.addLineOfText (context->getFont(), text, (float) startX, (float) baselineY);
        arr.draw (*this);
    }
}
예제 #8
0
void UploadWindow::paint(Graphics& g)
{
	g.fillAll(Colours::black);

	Colour border = Colours::wheat;
	juce::Rectangle<int> rc(0, 0, getWidth(), getHeight());
	for (int i = 0; i < 4; i++)
	{
		g.setColour(i == 0 ? Colours::black : border);
		g.drawRect(rc.getX(), rc.getY(), rc.getWidth(), rc.getHeight());
		rc.reduce(1, 1);
		border = border.brighter(0.4f);
	}
	
	ColourGradient gf(Colours::red, 0, getHeight()/2.0f, Colours::darkred, float(getWidth()), getHeight()/2.0f, false);
	FillType ft(gf);

	int cx = getWidth() / 2;
	int cy = getHeight() / 2;
	const float r = 12.0f;
	for (int i = 3; i >= 0; i--)
	{
		if (i % 2 != 0)
			g.setFillType(ft);
		else
			g.setColour(Colours::white);
		
		g.fillEllipse(cx - (r * i), cy - (r * i), (r * i) * 2, (r * i) * 2);
	}
	g.setFillType(Colours::transparentWhite);

	if (smugMug.isUploading())
	{
		int64 n = smugMug.getTotalbytesUploaded();
		int64 d = smugMug.getTotalBytesToUpload();
		double percent = (d == 0) ? 0 : (double(n)/double(d)*100);
		

		GlyphArrangement glyphs;
		glyphs.addLineOfText(Font(25.0f, Font::bold), String(percent, 1) + ("%"), 0, 0);

		Path p;
		glyphs.createPath(p);

		juce::Rectangle<float> bounds = p.getBounds();
		float cx = getWidth() / 2.0f - bounds.getWidth() / 2.0f - bounds.getX();
		float cy = getHeight() / 2.0f - bounds.getHeight() / 2.0f - bounds.getY();

		AffineTransform trans = AffineTransform::translation(cx, cy);
		g.setColour(Colours::black);
		g.fillPath(p, trans);
		g.setColour(Colours::white);
		g.strokePath(p, PathStrokeType(1), trans);
	}
}
예제 #9
0
Rectangle<int> IntrojucerLookAndFeel::getTabButtonExtraComponentBounds (const TabBarButton& button, Rectangle<int>& textArea, Component& comp)
{
    GlyphArrangement textLayout;
    createTabTextLayout (button, textArea, textLayout);
    const int textWidth = (int) textLayout.getBoundingBox (0, -1, false).getWidth();
    const int extraSpace = jmax (0, textArea.getWidth() - (textWidth + comp.getWidth())) / 2;

    textArea.removeFromRight (extraSpace);
    textArea.removeFromLeft (extraSpace);
    return textArea.removeFromRight (comp.getWidth());
}
예제 #10
0
void Graphics::drawMultiLineText (const String& text, const int startX, const int baselineY, const int maximumLineWidth) const
{
    if (text.isNotEmpty()
         && startX < context->getClipBounds().getRight())
    {
        GlyphArrangement arr;
        arr.addJustifiedText (context->getFont(), text,
                              (float) startX, (float) baselineY, (float) maximumLineWidth,
                              Justification::left);
        arr.draw (*this);
    }
}
예제 #11
0
    void paint (Graphics& g) override
    {
        double startTime = 0.0;

        {
            // A ScopedSaveState will return the Graphics context to the state it was at the time of
            // construction when it goes out of scope. We use it here to avoid clipping the fps text
            const Graphics::ScopedSaveState state (g);

            if (controls.clipToRectangle.getToggleState())  clipToRectangle (g);
            if (controls.clipToPath     .getToggleState())  clipToPath (g);
            if (controls.clipToImage    .getToggleState())  clipToImage (g);

            g.setImageResamplingQuality (controls.quality.getToggleState() ? Graphics::highResamplingQuality
                                                                           : Graphics::mediumResamplingQuality);

            // take a note of the time before the render
            startTime = Time::getMillisecondCounterHiRes();

            // then let the demo draw itself..
            drawDemo (g);
        }

        double now = Time::getMillisecondCounterHiRes();
        double filtering = 0.08;

        const double elapsedMs = now - startTime;
        averageTimeMs += (elapsedMs - averageTimeMs) * filtering;

        const double sinceLastRender = now - lastRenderStartTime;
        lastRenderStartTime = now;

        const double effectiveFPS = 1000.0 / averageTimeMs;
        const double actualFPS = sinceLastRender > 0 ? (1000.0 / sinceLastRender) : 0;
        averageActualFPS += (actualFPS - averageActualFPS) * filtering;

        GlyphArrangement ga;
        ga.addFittedText (displayFont,
                          "Time: " + String (averageTimeMs, 2)
                            + " ms\nEffective FPS: " + String (effectiveFPS, 1)
                            + "\nActual FPS: " + String (averageActualFPS, 1),
                          0, 10.0f, getWidth() - 10.0f, (float) getHeight(), Justification::topRight, 3);

        g.setColour (Colours::white.withAlpha (0.5f));
        g.fillRect (ga.getBoundingBox (0, ga.getNumGlyphs(), true).getSmallestIntegerContainer().expanded (4));

        g.setColour (Colours::black);
        ga.draw (g);
    }
void Graphics::drawText (const String& text, const Rectangle<float>& area,
                         Justification justificationType, bool useEllipsesIfTooBig) const
{
    if (text.isNotEmpty() && context.clipRegionIntersects (area.getSmallestIntegerContainer()))
    {
        GlyphArrangement arr;
        arr.addCurtailedLineOfText (context.getFont(), text, 0.0f, 0.0f,
                                    area.getWidth(), useEllipsesIfTooBig);

        arr.justifyGlyphs (0, arr.getNumGlyphs(),
                           area.getX(), area.getY(), area.getWidth(), area.getHeight(),
                           justificationType);
        arr.draw (*this);
    }
}
void GlyphArrangement::addLinesWithLineBreaks (const String& text, const Font& f,
                                               float x, float y, float width, float height, Justification layout)
{
    GlyphArrangement ga;
    ga.addJustifiedText (f, text, x, y, width, layout);

    const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));

    float dy = y - bb.getY();

    if (layout.testFlags (Justification::verticallyCentred))   dy += (height - bb.getHeight()) * 0.5f;
    else if (layout.testFlags (Justification::bottom))         dy += (height - bb.getHeight());

    ga.moveRangeOfGlyphs (0, -1, 0.0f, dy);

    glyphs.addArray (ga.glyphs);
}
예제 #14
0
void IntrojucerLookAndFeel::createTabTextLayout (const TabBarButton& button, const Rectangle<int>& textArea, GlyphArrangement& textLayout)
{
    Font font (textArea.getHeight() * 0.5f);
    font.setUnderline (button.hasKeyboardFocus (false));

    textLayout.addFittedText (font, button.getButtonText().trim(),
                              (float) textArea.getX(), (float) textArea.getY(), (float) textArea.getWidth(), (float) textArea.getHeight(),
                              Justification::centred, 1);
}
void Graphics::drawFittedText (const String& text, const Rectangle<int>& area,
                               const Justification& justification,
                               const int maximumNumberOfLines,
                               const float minimumHorizontalScale) const
{
    if (text.isNotEmpty() && (! area.isEmpty()) && context.clipRegionIntersects (area))
    {
        GlyphArrangement arr;
        arr.addFittedText (context.getFont(), text,
                           (float) area.getX(), (float) area.getY(),
                           (float) area.getWidth(), (float) area.getHeight(),
                           justification,
                           maximumNumberOfLines,
                           minimumHorizontalScale);

        arr.draw (*this);
    }
}
예제 #16
0
void Graphics::drawText (const String& text,
                         const int x, const int y, const int width, const int height,
                         const Justification& justificationType,
                         const bool useEllipsesIfTooBig) const
{
    if (text.isNotEmpty() && context->clipRegionIntersects (Rectangle<int> (x, y, width, height)))
    {
        GlyphArrangement arr;

        arr.addCurtailedLineOfText (context->getFont(), text,
                                    0.0f, 0.0f, (float) width,
                                    useEllipsesIfTooBig);

        arr.justifyGlyphs (0, arr.getNumGlyphs(),
                           (float) x, (float) y, (float) width, (float) height,
                           justificationType);
        arr.draw (*this);
    }
}
예제 #17
0
void IntrojucerLookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown)
{
    const Rectangle<int> activeArea (button.getActiveArea());

    const Colour bkg (getTabBackgroundColour (button));

    g.setGradientFill (ColourGradient (bkg.brighter (0.1f), 0, (float) activeArea.getY(),
                                       bkg.darker (0.1f), 0, (float) activeArea.getBottom(), false));
    g.fillRect (activeArea);

    g.setColour (button.findColour (mainBackgroundColourId).darker (0.3f));
    g.drawRect (activeArea);

    GlyphArrangement textLayout;
    createTabTextLayout (button, button.getTextArea(), textLayout);

    const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f;
    g.setColour (bkg.contrasting().withMultipliedAlpha (alpha));
    textLayout.draw (g);
}
예제 #18
0
const AffineTransform DrawableText::getArrangementAndTransform (GlyphArrangement& glyphs) const
{
    const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength();
    const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength();

    glyphs.addFittedText (scaledFont, text, 0, 0, w, h, justification, 0x100000);

    return AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].getX(), resolvedPoints[0].getY(),
                                              w, 0, resolvedPoints[1].getX(), resolvedPoints[1].getY(),
                                              0, h, resolvedPoints[2].getX(), resolvedPoints[2].getY());
}
예제 #19
0
    void paint(Graphics& g)
    {
        // The following code is basically the same as
        // Graphics::drawMultiLineText but with centered text
        int startX = 20;
        int baselineY = getHeight() / 2;
        int maximumLineWidth = getWidth() - 40;

        LowLevelGraphicsContext& context = g.getInternalContext();

        if (textToShow.isNotEmpty() && startX < context.getClipBounds().getRight())
        {
            GlyphArrangement arr;
            arr.addJustifiedText(context.getFont(), textToShow,
                                 (float) startX, (float) baselineY,
                                 (float) maximumLineWidth,
                                 Justification::centred);
            arr.draw(g);
        }
    }
예제 #20
0
void Graphics::drawFittedText (const String& text,
                               const int x, const int y, const int width, const int height,
                               const Justification& justification,
                               const int maximumNumberOfLines,
                               const float minimumHorizontalScale) const
{
    if (text.isNotEmpty()
         && width > 0 && height > 0
         && context->clipRegionIntersects (Rectangle<int> (x, y, width, height)))
    {
        GlyphArrangement arr;

        arr.addFittedText (context->getFont(), text,
                           (float) x, (float) y, (float) width, (float) height,
                           justification,
                           maximumNumberOfLines,
                           minimumHorizontalScale);

        arr.draw (*this);
    }
}
예제 #21
0
const int EdoChatWindowMessage::getMessageHeight(const int parentWidth, const Justification justification)
{
	GlyphArrangement messageGlyphs;
	float top,left,right,bottom;

	applyOptions();
	messageGlyphs.clear();

	messageGlyphs.addJustifiedText (
										EdoRestoreFont (getPropertyString(EWND_MESSAGE_FONT), Font(14)), 
										messageLabel->getText(), 
										0, 
										0, 
										(float)parentWidth, 
										justification
									);

	messageGlyphs.getBoundingBox (0, -1, left, top, right, bottom, true);

	messageHeight = roundFloatToInt ((bottom - top) + SPACER);

	// Log (String::formatted (T("EdoChatWindowMessage::getMessageHeight parentWidth: %d height: %d"), parentWidth, messageHeight));

	if (avatar->isVisible())
	{
		if (messageHeight < (avatar->getHeight()+16))
		{
			messageHeight = avatar->getHeight()+16;
			return (messageHeight+SPACER);
		}
		else
		{
			return (messageHeight+SPACER);
		}
	}
	else
	{
		return (messageHeight+SPACER);
	}
}
void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected)
{
    if (rowIsSelected)
        g.fillAll (findColour (TextEditor::highlightColourId));

    String name, desc;
    bool isBlacklisted = false;

    if (row >= list.getNumTypes())
    {
        isBlacklisted = true;
        name = list.getBlacklistedFiles() [row - list.getNumTypes()];
        desc = TRANS("Deactivated after failing to initialise correctly");
    }
    else if (const PluginDescription* const pd = list.getType (row))
    {
        name = pd->name;

        desc << pd->pluginFormatName
             << (pd->isInstrument ? " instrument" : " effect")
             << " - " << pd->numInputChannels  << (pd->numInputChannels  == 1 ? " in"  : " ins")
             << " / " << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs");

        if (pd->manufacturerName.isNotEmpty())  desc << " - " << pd->manufacturerName;
        if (pd->version.isNotEmpty())           desc << " - " << pd->version;
        if (pd->category.isNotEmpty())          desc << " - category: '" << pd->category << '\'';
    }

    if (name.isNotEmpty())
    {
        GlyphArrangement ga;
        ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold),
                                   name, 8.0f, height * 0.8f, width - 10.0f, true);

        g.setColour (isBlacklisted ? Colours::red : Colours::black);
        ga.draw (g);

        const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));

        ga.clear();
        ga.addCurtailedLineOfText (Font (height * 0.6f), desc,
                                   jmax (bb.getRight() + 10.0f, width / 3.0f), height * 0.8f,
                                   width - bb.getRight() - 12.0f, true);

        g.setColour (isBlacklisted ? Colours::red : Colours::grey);
        ga.draw (g);
    }
}
void PluginListComponent::paintListBoxItem (int row,
                                            Graphics& g,
                                            int width, int height,
                                            bool rowIsSelected)
{
    if (rowIsSelected)
        g.fillAll (findColour (TextEditor::highlightColourId));

    const PluginDescription* const pd = list.getType (row);

    if (pd != 0)
    {
        GlyphArrangement ga;
        ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), pd->name, 8.0f, height * 0.8f, width - 10.0f, true);

        g.setColour (Colours::black);
        ga.draw (g);

        float x, y, r, b;
        ga.getBoundingBox (0, -1, x, y, r, b, false);

        String desc;
        desc << pd->pluginFormatName
             << (pd->isInstrument ? " instrument" : " effect")
             << " - "
             << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins")
             << " / "
             << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs");

        if (pd->manufacturerName.isNotEmpty())
            desc << " - " << pd->manufacturerName;

        if (pd->version.isNotEmpty())
            desc << " - " << pd->version;

         if (pd->category.isNotEmpty())
            desc << " - category: '" << pd->category << '\'';

        g.setColour (Colours::grey);

        ga.clear();
        ga.addCurtailedLineOfText (Font (height * 0.6f), desc, r + 10.0f, height * 0.8f, width - r - 12.0f, true);
        ga.draw (g);
    }
}
예제 #24
0
void GlyphArrangement::addFittedText (const Font& f,
                                      const String& text,
                                      const float x, const float y,
                                      const float width, const float height,
                                      const Justification& layout,
                                      int maximumLines,
                                      const float minimumHorizontalScale)
{
    // doesn't make much sense if this is outside a sensible range of 0.5 to 1.0
    jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f);

    if (text.containsAnyOf ("\r\n"))
    {
        GlyphArrangement ga;
        ga.addJustifiedText (f, text, x, y, width, layout);

        const Rectangle<float> bb (ga.getBoundingBox (0, -1, false));

        float dy = y - bb.getY();

        if (layout.testFlags (Justification::verticallyCentred))
            dy += (height - bb.getHeight()) * 0.5f;
        else if (layout.testFlags (Justification::bottom))
            dy += height - bb.getHeight();

        ga.moveRangeOfGlyphs (0, -1, 0.0f, dy);

        glyphs.ensureStorageAllocated (glyphs.size() + ga.glyphs.size());

        for (int i = 0; i < ga.glyphs.size(); ++i)
            glyphs.add (ga.glyphs.getUnchecked (i));

        ga.glyphs.clear (false);
        return;
    }

    int startIndex = glyphs.size();
    addLineOfText (f, text.trim(), x, y);

    if (glyphs.size() > startIndex)
    {
        float lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight()
                            - glyphs.getUnchecked (startIndex)->getLeft();

        if (lineWidth <= 0)
            return;

        if (lineWidth * minimumHorizontalScale < width)
        {
            if (lineWidth > width)
                stretchRangeOfGlyphs (startIndex, glyphs.size() - startIndex,
                                      width / lineWidth);

            justifyGlyphs (startIndex, glyphs.size() - startIndex,
                           x, y, width, height, layout);
        }
        else if (maximumLines <= 1)
        {
            fitLineIntoSpace (startIndex, glyphs.size() - startIndex,
                              x, y, width, height, f, layout, minimumHorizontalScale);
        }
        else
        {
            Font font (f);
            String txt (text.trim());
            const int length = txt.length();
            const int originalStartIndex = startIndex;
            int numLines = 1;

            if (length <= 12 && ! txt.containsAnyOf (" -\t\r\n"))
                maximumLines = 1;

            maximumLines = jmin (maximumLines, length);

            while (numLines < maximumLines)
            {
                ++numLines;

                const float newFontHeight = height / (float) numLines;

                if (newFontHeight < font.getHeight())
                {
                    font.setHeight (jmax (8.0f, newFontHeight));

                    removeRangeOfGlyphs (startIndex, -1);
                    addLineOfText (font, txt, x, y);

                    lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight()
                                    - glyphs.getUnchecked (startIndex)->getLeft();
                }

                if (numLines > lineWidth / width || newFontHeight < 8.0f)
                    break;
            }

            if (numLines < 1)
                numLines = 1;

            float lineY = y;
            float widthPerLine = lineWidth / numLines;
            int lastLineStartIndex = 0;

            for (int line = 0; line < numLines; ++line)
            {
                int i = startIndex;
                lastLineStartIndex = i;
                float lineStartX = glyphs.getUnchecked (startIndex)->getLeft();

                if (line == numLines - 1)
                {
                    widthPerLine = width;
                    i = glyphs.size();
                }
                else
                {
                    while (i < glyphs.size())
                    {
                        lineWidth = (glyphs.getUnchecked (i)->getRight() - lineStartX);

                        if (lineWidth > widthPerLine)
                        {
                            // got to a point where the line's too long, so skip forward to find a
                            // good place to break it..
                            const int searchStartIndex = i;

                            while (i < glyphs.size())
                            {
                                if ((glyphs.getUnchecked (i)->getRight() - lineStartX) * minimumHorizontalScale < width)
                                {
                                    if (glyphs.getUnchecked (i)->isWhitespace()
                                         || glyphs.getUnchecked (i)->getCharacter() == '-')
                                    {
                                        ++i;
                                        break;
                                    }
                                }
                                else
                                {
                                    // can't find a suitable break, so try looking backwards..
                                    i = searchStartIndex;

                                    for (int back = 1; back < jmin (5, i - startIndex - 1); ++back)
                                    {
                                        if (glyphs.getUnchecked (i - back)->isWhitespace()
                                             || glyphs.getUnchecked (i - back)->getCharacter() == '-')
                                        {
                                            i -= back - 1;
                                            break;
                                        }
                                    }

                                    break;
                                }

                                ++i;
                            }

                            break;
                        }

                        ++i;
                    }

                    int wsStart = i;
                    while (wsStart > 0 && glyphs.getUnchecked (wsStart - 1)->isWhitespace())
                        --wsStart;

                    int wsEnd = i;

                    while (wsEnd < glyphs.size() && glyphs.getUnchecked (wsEnd)->isWhitespace())
                        ++wsEnd;

                    removeRangeOfGlyphs (wsStart, wsEnd - wsStart);
                    i = jmax (wsStart, startIndex + 1);
                }

                i -= fitLineIntoSpace (startIndex, i - startIndex,
                                       x, lineY, width, font.getHeight(), font,
                                       layout.getOnlyHorizontalFlags() | Justification::verticallyCentred,
                                       minimumHorizontalScale);

                startIndex = i;
                lineY += font.getHeight();

                if (startIndex >= glyphs.size())
                    break;
            }

            justifyGlyphs (originalStartIndex, glyphs.size() - originalStartIndex,
                           x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified);
        }
    }
}