blob: 6cd9e194d80a7253957aaeafac5cf2b9316fd826 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.css2.layout;
import java.text.BreakIterator;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
/**
* Utility class for FlowFigures.
*
*/
public final class FlowUtilities extends FigureUtilities {
/**
* Returns the number of characters from the specified String that will fit
* in the available amount of space. An average character width can be
* provided as a hint for faster calculation.
*
* @param frag
* the TextFragmentBox
* @param string
* the String
* @param font
* the Font used for measuring
* @param availableWidth
* the available width in pixels
* @param avg
* 0.0, or an avg character width to use during calculation
* @param wrapping
* the word wrap style
* @return the number of characters that will fit in the space
*/
public static int setupFragmentBasedOnTextSpace(TextFragmentBox frag,
String string, Font font, int availableWidth, float avg,
int wrapping) {
int result = getTextForSpace(string, font, availableWidth, avg,
wrapping);
frag._length = result;
setupFragment(frag, font, string);
return result;
}
/**
* given the text string, font and available width and wrapping mode.
* Calculate how much text can fit into.
*
* @param string
* @param font
* @param availableWidth
* @param avg
* @param wrapping
* @return how much text can fit into
*/
public static int getTextForSpace(String string, Font font,
int availableWidth, float avg, int wrapping) {
if (string.length() == 0) {
return 0;
}
FontMetrics metrics = getFontMetrics(font);
BreakIterator breakItr = BreakIterator.getLineInstance();
breakItr.setText(string);
int MIN, min, max;
if (avg == 0.0) {
avg = metrics.getAverageCharWidth();
}
int firstBreak = breakItr.next();
int winNL = string.indexOf("\r\n"); //$NON-NLS-1$
int macNL = string.indexOf('\r');
int unixNL = string.indexOf('\n');
MIN = min = (wrapping == CSSTextLayout.WORD_WRAP_HARD) ? firstBreak : 1;
if (macNL == winNL) {
macNL = -1; // If the Mac newline is just the prefix to the win NL,
// ignore it
}
max = string.length() + 1;
if (winNL != -1) {
max = Math.min(max, winNL);
min = Math.min(min, winNL);
}
if (unixNL != -1) {
max = Math.min(max, unixNL);
min = Math.min(min, unixNL);
}
if (macNL != -1) {
max = Math.min(max, macNL);
min = Math.min(min, macNL);
}
int origMax = max;
// The size of the current guess
int guess = 0, guessSize = 0;
while ((max - min) > 1) {
// Pick a new guess size
// New guess is the last guess plus the missing width in pixels
// divided by the average character size in pixels
guess = guess + (int) ((availableWidth - guessSize) / avg);
if (guess >= max) {
guess = max - 1;
}
if (guess <= min) {
guess = min + 1;
}
// Measure the current guess
guessSize = getStringExtents2(string.substring(0, guess), font).width;
if (guessSize <= availableWidth) {
// We did not use the available width
min = guess;
} else {
// We exceeded the available width
max = guess;
}
}
int result = string.length();
switch (wrapping) {
case CSSTextLayout.WORD_WRAP_HARD:
if (min == string.length() || min == winNL || min == unixNL
|| min == macNL) {
result = min;
} else if (max == origMax
&& getStringExtents2(string.substring(0, max), font).width <= availableWidth) {
result = max;
} else {
result = Math.max(MIN, breakItr.preceding(Math.min(max, string
.length() - 1)));
}
break;
case CSSTextLayout.WORD_WRAP_SOFT:
if (min == string.length() || min == winNL || min == unixNL
|| min == macNL) {
result = min;
} else if (max == origMax
&& getStringExtents2(string.substring(0, max), font).width <= availableWidth) {
result = max;
} else if (breakItr.isBoundary(min)) {
result = min;
} else if (breakItr.isBoundary(Math.min(max, string.length() - 1))) {
result = max;
} else {
result = breakItr.preceding(Math.min(max, string.length() - 1));
}
if (result <= 0) {
result = min;
}
break;
// case CSSTextLayout.WORD_WRAP_TRUNCATE:
// if (min == string.length() || min == winNL || min == unixNL || min ==
// macNL)
// {
// result = frag._length = min;
// setupFragment(frag, font, string);
// if (frag.getWidth() <= availableWidth)
// return result;
// min -= 1;
// }
// else if (max == origMax && getStringExtents(string.substring(0, max),
// font).width <= availableWidth)
// {
// result = frag._length = max;
// setupFragment(frag, font, string);
// return result;
// }
// result = breakItr.preceding(Math.min(max + 1, string.length() - 1));
// if (result <= 0)
// {
// ELLIPSIS_SIZE =
// FigureUtilities.getStringExtents(CSSTextFigure.ELLIPSIS, font);
// getTextForSpace(frag, string, font, availableWidth -
// ELLIPSIS_SIZE.width, avg, CSSTextLayout.WORD_WRAP_SOFT);
// //frag.length = min;
// frag._truncated = true;
// result = breakItr.following(min);
// if (result == BreakIterator.DONE)
// result = string.length();
// }
// else
// {
// frag._length = result;
// }
}
return result;
}
/**
* @param string
* @param font
* @param availableWidth
* @param avg
* @return the text width
*/
public static int getTextInWidth(String string, Font font,
int availableWidth, float avg) {
if (string.length() == 0) {
return 0;
}
int guess = 0;
while (true) {
Dimension a = getTextExtents(string.substring(0, guess), font);
if (a.width >= availableWidth) {
return guess;
}
guess++;
if (guess == string.length()) {
return guess;
}
}
}
/**
* change the parent implementation of getStringExtents(). Don't expend the
* 1 width. So empty string will not have any width.
*
* @param s
* @param f
* @return the dimension
*/
public static Dimension getStringExtents2(String s, Font f) {
return new Dimension(getStringDimension(s, f));
}
static void setupFragment(TextFragmentBox frag, Font f, String s) {
// if (frag.length != s.length())
// we don't skip whitespace here. since already truncated in
// CSSTextLayout
// while (frag.length > 0 &&
// Character.isElementContentWhitespace(s.charAt(frag.length - 1)))
// frag.length--;
frag.setTextData(s.substring(0, frag._length));
Dimension d = getStringExtents2(s.substring(0, frag._length), f);
FontMetrics fm = getFontMetrics(f);
frag.setHeight(fm.getHeight());
frag.setAscent(fm.getAscent() + fm.getLeading());
if (frag._length > 0
&& Character.isWhitespace(s.charAt(frag._length - 1))) {
frag._isLastCharWhitespace = true;
} else {
frag._isLastCharWhitespace = false;
}
frag.setWidth(d.width);
}
private FlowUtilities()
{
// no instantiation
}
}