2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.util.ui;
18 import com.intellij.ide.BrowserUtil;
19 import com.intellij.openapi.actionSystem.ActionManager;
20 import com.intellij.openapi.actionSystem.AnAction;
21 import com.intellij.openapi.actionSystem.AnActionEvent;
22 import com.intellij.openapi.actionSystem.DefaultActionGroup;
23 import com.intellij.openapi.application.ApplicationManager;
24 import com.intellij.openapi.application.ModalityState;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
27 import com.intellij.openapi.fileChooser.FileChooserFactory;
28 import com.intellij.openapi.ide.CopyPasteManager;
29 import com.intellij.openapi.options.ex.SingleConfigurableEditor;
30 import com.intellij.openapi.options.newEditor.SettingsDialog;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.ui.*;
33 import com.intellij.openapi.util.SystemInfo;
34 import com.intellij.openapi.util.text.StringUtil;
35 import com.intellij.ui.HyperlinkLabel;
36 import com.intellij.ui.TextFieldWithHistory;
37 import com.intellij.ui.TextFieldWithHistoryWithBrowseButton;
38 import com.intellij.util.Consumer;
39 import com.intellij.util.NotNullProducer;
40 import com.intellij.util.ObjectUtils;
41 import com.intellij.util.PlatformIcons;
42 import com.intellij.util.containers.ComparatorUtil;
43 import com.intellij.util.containers.ContainerUtil;
44 import com.intellij.util.ui.update.UiNotifyConnector;
45 import org.jetbrains.annotations.Nls;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
50 import javax.swing.event.PopupMenuEvent;
51 import javax.swing.event.PopupMenuListener;
52 import javax.swing.table.DefaultTableCellRenderer;
53 import javax.swing.table.TableCellRenderer;
54 import javax.swing.table.TableColumn;
56 import java.awt.datatransfer.StringSelection;
57 import java.awt.datatransfer.Transferable;
58 import java.awt.event.ActionListener;
59 import java.util.Collection;
60 import java.util.List;
63 public class SwingHelper {
65 private static final Logger LOG = Logger.getInstance(SwingHelper.class);
66 private static final String DIALOG_RESIZED_TO_FIT_TEXT = "INTELLIJ_DIALOG_RESIZED_TO_FIT_TEXT";
69 * Creates panel whose content consists of given {@code children} components
70 * stacked vertically each on another in a given order.
72 * @param childAlignmentX Component.LEFT_ALIGNMENT, Component.CENTER_ALIGNMENT or Component.RIGHT_ALIGNMENT
73 * @param children children components
74 * @return created panel
77 public static JPanel newVerticalPanel(float childAlignmentX, Component... children) {
78 return newGenericBoxPanel(true, childAlignmentX, children);
82 public static JPanel newLeftAlignedVerticalPanel(Component... children) {
83 return newVerticalPanel(Component.LEFT_ALIGNMENT, children);
87 public static JPanel newLeftAlignedVerticalPanel(@NotNull Collection<Component> children) {
88 return newVerticalPanel(Component.LEFT_ALIGNMENT, children);
92 public static JPanel newVerticalPanel(float childAlignmentX, @NotNull Collection<Component> children) {
93 return newVerticalPanel(childAlignmentX, children.toArray(new Component[children.size()]));
97 * Creates panel whose content consists of given {@code children} components horizontally
98 * stacked each on another in a given order.
100 * @param childAlignmentY Component.TOP_ALIGNMENT, Component.CENTER_ALIGNMENT or Component.BOTTOM_ALIGNMENT
101 * @param children children components
102 * @return created panel
105 public static JPanel newHorizontalPanel(float childAlignmentY, Component... children) {
106 return newGenericBoxPanel(false, childAlignmentY, children);
110 public static JPanel newHorizontalPanel(float childAlignmentY, @NotNull Collection<Component> children) {
111 return newHorizontalPanel(childAlignmentY, children.toArray(new Component[children.size()]));
114 private static JPanel newGenericBoxPanel(boolean verticalOrientation,
115 float childAlignment,
116 Component... children) {
117 JPanel panel = new JPanel();
118 int axis = verticalOrientation ? BoxLayout.Y_AXIS : BoxLayout.X_AXIS;
119 panel.setLayout(new BoxLayout(panel, axis));
120 for (Component child : children) {
121 panel.add(child, childAlignment);
122 if (child instanceof JComponent) {
123 JComponent jChild = (JComponent)child;
124 if (verticalOrientation) {
125 jChild.setAlignmentX(childAlignment);
128 jChild.setAlignmentY(childAlignment);
136 public static JPanel wrapWithoutStretch(@NotNull JComponent component) {
137 JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
138 panel.add(component);
143 public static JPanel wrapWithHorizontalStretch(@NotNull JComponent component) {
144 JPanel panel = new JPanel(new BorderLayout(0, 0));
145 panel.add(component, BorderLayout.NORTH);
149 public static void setPreferredWidthToFitText(@NotNull TextFieldWithHistoryWithBrowseButton component) {
150 int childWidth = calcWidthToFitText(component.getChildComponent().getTextEditor(), JBUI.scale(32));
151 setPreferredWidthForComponentWithBrowseButton(component, childWidth);
154 public static void setPreferredWidthToFitText(@NotNull TextFieldWithBrowseButton component) {
155 int childWidth = calcWidthToFitText(component.getChildComponent(), JBUI.scale(20));
156 setPreferredWidthForComponentWithBrowseButton(component, childWidth);
159 private static <T extends JComponent> void setPreferredWidthForComponentWithBrowseButton(@NotNull ComponentWithBrowseButton<T> component,
160 int childPrefWidth) {
161 Dimension buttonPrefSize = component.getButton().getPreferredSize();
162 setPreferredWidth(component, childPrefWidth + buttonPrefSize.width);
165 public static void setPreferredWidthToFitText(@NotNull JTextField textField) {
166 setPreferredWidthToFitText(textField, JBUI.scale(15));
169 public static void setPreferredWidthToFitText(@NotNull JTextField textField, int additionalWidth) {
170 setPreferredSizeToFitText(textField, StringUtil.notNullize(textField.getText()), additionalWidth);
173 public static void setPreferredWidthToFitText(@NotNull JTextField textField, @NotNull String text) {
174 setPreferredSizeToFitText(textField, text, JBUI.scale(15));
177 private static void setPreferredSizeToFitText(@NotNull JTextField textField, @NotNull String text, int additionalWidth) {
178 int width = calcWidthToFitText(textField, text, additionalWidth);
179 setPreferredWidth(textField, width);
182 private static int calcWidthToFitText(@NotNull JTextField textField, int additionalWidth) {
183 return calcWidthToFitText(textField, textField.getText(), additionalWidth);
186 private static int calcWidthToFitText(@NotNull JTextField textField, @NotNull String text, int additionalWidth) {
187 return textField.getFontMetrics(textField.getFont()).stringWidth(text) + additionalWidth;
190 public static void adjustDialogSizeToFitPreferredSize(@NotNull DialogWrapper dialogWrapper) {
191 JRootPane rootPane = dialogWrapper.getRootPane();
192 Dimension componentSize = rootPane.getSize();
193 Dimension componentPreferredSize = rootPane.getPreferredSize();
194 if (componentPreferredSize.width <= componentSize.width && componentPreferredSize.height <= componentSize.height) {
197 int dw = Math.max(0, componentPreferredSize.width - componentSize.width);
198 int dh = Math.max(0, componentPreferredSize.height - componentSize.height);
200 Dimension oldDialogSize = dialogWrapper.getSize();
201 Dimension newDialogSize = new Dimension(oldDialogSize.width + dw, oldDialogSize.height + dh);
203 dialogWrapper.setSize(newDialogSize.width, newDialogSize.height);
204 rootPane.revalidate();
207 LOG.info("DialogWrapper '" + dialogWrapper.getTitle() + "' has been re-sized (added width: " + dw + ", added height: " + dh + ")");
210 public static void resizeDialogToFitTextFor(@NotNull final JComponent... components) {
211 if (components.length == 0) return;
212 doWithDialogWrapper(components[0], new Consumer<DialogWrapper>() {
214 public void consume(final DialogWrapper dialogWrapper) {
215 if (dialogWrapper instanceof SettingsDialog || dialogWrapper instanceof SingleConfigurableEditor) {
216 for (Component component : components) {
217 if (component instanceof TextFieldWithHistoryWithBrowseButton) {
218 setPreferredWidthToFitText((TextFieldWithHistoryWithBrowseButton)component);
220 else if (component instanceof TextFieldWithBrowseButton) {
221 setPreferredWidthToFitText((TextFieldWithBrowseButton)component);
223 else if (component instanceof JTextField) {
224 setPreferredWidthToFitText((JTextField)component);
227 ApplicationManager.getApplication().invokeLater(new Runnable() {
230 adjustDialogSizeToFitPreferredSize(dialogWrapper);
232 }, ModalityState.any());
238 private static void doWithDialogWrapper(@NotNull final JComponent component, @NotNull final Consumer<DialogWrapper> consumer) {
239 UIUtil.invokeLaterIfNeeded(new Runnable() {
242 if (component.getClientProperty(DIALOG_RESIZED_TO_FIT_TEXT) != null) {
245 component.putClientProperty(DIALOG_RESIZED_TO_FIT_TEXT, true);
246 DialogWrapper dialogWrapper = DialogWrapper.findInstance(component);
247 if (dialogWrapper != null) {
248 consumer.consume(dialogWrapper);
251 UiNotifyConnector.doWhenFirstShown(component, new Runnable() {
254 DialogWrapper dialogWrapper = DialogWrapper.findInstance(component);
255 if (dialogWrapper != null) {
256 consumer.consume(dialogWrapper);
265 public static <T> void updateItems(@NotNull JComboBox comboBox,
266 @NotNull List<T> newItems,
267 @Nullable T newSelectedItemIfSelectionCannotBePreserved) {
268 if (!shouldUpdate(comboBox, newItems)) {
271 Object selectedItem = comboBox.getSelectedItem();
272 //noinspection SuspiciousMethodCalls
273 if (selectedItem != null && !newItems.contains(selectedItem)) {
276 if (selectedItem == null && newItems.contains(newSelectedItemIfSelectionCannotBePreserved)) {
277 selectedItem = newSelectedItemIfSelectionCannotBePreserved;
279 comboBox.removeAllItems();
280 for (T newItem : newItems) {
281 comboBox.addItem(newItem);
283 if (selectedItem != null) {
284 int count = comboBox.getItemCount();
285 for (int i = 0; i < count; i++) {
286 Object item = comboBox.getItemAt(i);
287 if (selectedItem.equals(item)) {
288 comboBox.setSelectedIndex(i);
295 private static <T> boolean shouldUpdate(@NotNull JComboBox comboBox, @NotNull List<T> newItems) {
296 int count = comboBox.getItemCount();
297 if (newItems.size() != count) {
300 for (int i = 0; i < count; i++) {
301 Object oldItem = comboBox.getItemAt(i);
302 T newItem = newItems.get(i);
303 if (!ComparatorUtil.equalsNullable(oldItem, newItem)) {
310 public static void setNoBorderCellRendererFor(@NotNull TableColumn column) {
311 final TableCellRenderer previous = column.getCellRenderer();
312 column.setCellRenderer(new DefaultTableCellRenderer() {
314 public Component getTableCellRendererComponent(JTable table,
321 if (previous != null) {
322 component = previous.getTableCellRendererComponent(table, value, isSelected, false, row, column);
325 component = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
327 if (component instanceof JComponent) {
328 ((JComponent)component).setBorder(null);
335 public static void addHistoryOnExpansion(@NotNull final TextFieldWithHistory textFieldWithHistory,
336 @NotNull final NotNullProducer<List<String>> historyProvider) {
337 textFieldWithHistory.addPopupMenuListener(new PopupMenuListener() {
339 public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
340 List<String> history = historyProvider.produce();
341 setHistory(textFieldWithHistory, ContainerUtil.notNullize(history), true);
342 // one-time initialization
343 textFieldWithHistory.removePopupMenuListener(this);
347 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
351 public void popupMenuCanceled(PopupMenuEvent e) {
356 public static void setHistory(@NotNull TextFieldWithHistory textFieldWithHistory,
357 @NotNull List<String> history,
358 boolean mergeWithPrevHistory) {
359 Set<String> newHistorySet = ContainerUtil.newHashSet(history);
360 List<String> prevHistory = textFieldWithHistory.getHistory();
361 List<String> mergedHistory = ContainerUtil.newArrayListWithCapacity(history.size());
362 if (mergeWithPrevHistory) {
363 for (String item : prevHistory) {
364 if (!newHistorySet.contains(item)) {
365 mergedHistory.add(item);
369 mergedHistory.addAll(history);
370 String oldText = StringUtil.notNullize(textFieldWithHistory.getText());
371 String oldSelectedItem = ObjectUtils.tryCast(textFieldWithHistory.getSelectedItem(), String.class);
372 if (!mergedHistory.contains(oldSelectedItem)) {
373 oldSelectedItem = null;
375 textFieldWithHistory.setHistory(mergedHistory);
376 setLongestAsPrototype(textFieldWithHistory, mergedHistory);
377 if (oldSelectedItem != null) {
378 textFieldWithHistory.setSelectedItem(oldSelectedItem);
380 if (!oldText.equals(oldSelectedItem)) {
381 textFieldWithHistory.setText(oldText);
385 private static void setLongestAsPrototype(@NotNull JComboBox comboBox, @NotNull List<String> variants) {
386 Object prototypeDisplayValue = comboBox.getPrototypeDisplayValue();
387 String prototypeDisplayValueStr = null;
388 if (prototypeDisplayValue instanceof String) {
389 prototypeDisplayValueStr = (String)prototypeDisplayValue;
391 else if (prototypeDisplayValue != null) {
394 String longest = StringUtil.notNullize(prototypeDisplayValueStr);
395 boolean updated = false;
396 for (String s : variants) {
397 if (longest.length() < s.length()) {
403 comboBox.setPrototypeDisplayValue(longest);
407 public static void installFileCompletionAndBrowseDialog(@Nullable Project project,
408 @NotNull TextFieldWithHistoryWithBrowseButton textFieldWithHistoryWithBrowseButton,
409 @NotNull @Nls(capitalization = Nls.Capitalization.Title) String browseDialogTitle,
410 @NotNull FileChooserDescriptor fileChooserDescriptor) {
412 textFieldWithHistoryWithBrowseButton,
413 textFieldWithHistoryWithBrowseButton.getChildComponent().getTextEditor(),
415 fileChooserDescriptor,
416 TextComponentAccessor.TEXT_FIELD_WITH_HISTORY_WHOLE_TEXT);
419 public static void installFileCompletionAndBrowseDialog(@Nullable Project project,
420 @NotNull TextFieldWithBrowseButton textFieldWithBrowseButton,
421 @NotNull @Nls(capitalization = Nls.Capitalization.Title) String browseDialogTitle,
422 @NotNull FileChooserDescriptor fileChooserDescriptor) {
424 textFieldWithBrowseButton,
425 textFieldWithBrowseButton.getTextField(),
427 fileChooserDescriptor,
428 TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
431 private static <T extends JComponent> void doInstall(@Nullable Project project,
432 @NotNull ComponentWithBrowseButton<T> componentWithBrowseButton,
433 @NotNull JTextField textField,
434 @NotNull @Nls(capitalization = Nls.Capitalization.Title) String browseDialogTitle,
435 @NotNull FileChooserDescriptor fileChooserDescriptor,
436 @NotNull TextComponentAccessor<T> textComponentAccessor) {
437 fileChooserDescriptor = fileChooserDescriptor.withShowHiddenFiles(SystemInfo.isUnix);
438 componentWithBrowseButton.addBrowseFolderListener(
440 new ComponentWithBrowseButton.BrowseFolderActionListener<T>(
443 componentWithBrowseButton,
445 fileChooserDescriptor,
446 textComponentAccessor
450 FileChooserFactory.getInstance().installFileCompletion(
452 fileChooserDescriptor,
459 public static HyperlinkLabel createWebHyperlink(@NotNull String url) {
460 return createWebHyperlink(url, url);
464 public static HyperlinkLabel createWebHyperlink(@NotNull String text, @NotNull String url) {
465 HyperlinkLabel hyperlink = new HyperlinkLabel(text);
466 hyperlink.setHyperlinkTarget(url);
468 DefaultActionGroup actionGroup = new DefaultActionGroup();
469 actionGroup.add(new OpenLinkInBrowser(url));
470 actionGroup.add(new CopyLinkAction(url));
472 hyperlink.setComponentPopupMenu(ActionManager.getInstance().createActionPopupMenu("web hyperlink", actionGroup).getComponent());
476 public static void setPreferredWidth(@NotNull Component component, int width) {
477 Dimension preferredSize = component.getPreferredSize();
478 preferredSize.width = width;
479 component.setPreferredSize(preferredSize);
482 public static class HtmlViewerBuilder {
483 private boolean myCarryTextOver;
484 private String myDisabledHtml;
486 private Color myBackground;
487 private Color myForeground;
489 public JEditorPane create() {
490 final JEditorPane textPane = new JEditorPane() {
491 private boolean myEnabled = true;
492 private String myEnabledHtml;
495 public Dimension getPreferredSize() {
496 // This trick makes text component to carry text over to the next line
497 // if the text line width exceeds parent's width
498 Dimension dimension = super.getPreferredSize();
499 if (myCarryTextOver) {
506 public void setText(String t) {
507 if (myDisabledHtml != null) {
516 public void setEnabled(boolean enabled) {
517 if (myDisabledHtml != null) {
520 setText(myEnabledHtml);
522 setText(myDisabledHtml);
524 super.setEnabled(true);
526 super.setEnabled(enabled);
530 textPane.setFont(myFont != null ? myFont : UIUtil.getLabelFont());
531 textPane.setContentType(UIUtil.HTML_MIME);
532 textPane.setEditable(false);
533 if (myBackground != null) {
534 textPane.setBackground(myBackground);
537 textPane.setOpaque(false);
539 textPane.setForeground(myForeground != null ? myForeground : UIUtil.getLabelForeground());
540 textPane.setFocusable(false);
544 public HtmlViewerBuilder setCarryTextOver(boolean carryTextOver) {
545 myCarryTextOver = carryTextOver;
549 public HtmlViewerBuilder setDisabledHtml(String disabledHtml) {
550 myDisabledHtml = disabledHtml;
554 public HtmlViewerBuilder setFont(Font font) {
559 public HtmlViewerBuilder setBackground(Color background) {
560 myBackground = background;
564 public HtmlViewerBuilder setForeground(Color foreground) {
565 myForeground = foreground;
571 public static JEditorPane createHtmlViewer(boolean carryTextOver,
573 @Nullable Color background,
574 @Nullable Color foreground) {
575 final JEditorPane textPane;
577 textPane = new JEditorPane() {
579 public Dimension getPreferredSize() {
580 // This trick makes text component to carry text over to the next line
581 // if the text line width exceeds parent's width
582 Dimension dimension = super.getPreferredSize();
589 textPane = new JEditorPane();
591 textPane.setFont(font != null ? font : UIUtil.getLabelFont());
592 textPane.setContentType(UIUtil.HTML_MIME);
593 textPane.setEditable(false);
594 if (background != null) {
595 textPane.setBackground(background);
598 textPane.setOpaque(false);
600 textPane.setForeground(foreground != null ? foreground : UIUtil.getLabelForeground());
601 textPane.setFocusable(false);
605 public static void setHtml(@NotNull JEditorPane editorPane,
606 @NotNull String bodyInnerHtml,
607 @Nullable Color foregroundColor) {
608 String html = String.format(
609 "<html><head>%s</head><body>%s</body></html>",
610 UIUtil.getCssFontDeclaration(editorPane.getFont(), foregroundColor, null, null),
613 editorPane.setText(html);
617 public static TextFieldWithHistoryWithBrowseButton createTextFieldWithHistoryWithBrowseButton(@Nullable Project project,
618 @NotNull String browseDialogTitle,
619 @NotNull FileChooserDescriptor fileChooserDescriptor,
620 @Nullable NotNullProducer<List<String>> historyProvider) {
621 TextFieldWithHistoryWithBrowseButton textFieldWithHistoryWithBrowseButton = new TextFieldWithHistoryWithBrowseButton();
622 TextFieldWithHistory textFieldWithHistory = textFieldWithHistoryWithBrowseButton.getChildComponent();
623 textFieldWithHistory.setHistorySize(-1);
624 textFieldWithHistory.setMinimumAndPreferredWidth(0);
625 if (historyProvider != null) {
626 addHistoryOnExpansion(textFieldWithHistory, historyProvider);
628 installFileCompletionAndBrowseDialog(
630 textFieldWithHistoryWithBrowseButton,
632 fileChooserDescriptor
634 return textFieldWithHistoryWithBrowseButton;
638 public static <C extends JComponent> ComponentWithBrowseButton<C> wrapWithInfoButton(@NotNull final C component,
639 @NotNull String infoButtonTooltip,
640 @NotNull ActionListener listener) {
641 ComponentWithBrowseButton<C> comp = new ComponentWithBrowseButton<C>(component, listener);
642 FixedSizeButton uiHelpButton = comp.getButton();
643 uiHelpButton.setToolTipText(infoButtonTooltip);
644 uiHelpButton.setIcon(UIUtil.getBalloonInformationIcon());
645 uiHelpButton.setHorizontalAlignment(SwingConstants.CENTER);
646 uiHelpButton.setVerticalAlignment(SwingConstants.CENTER);
650 private static class CopyLinkAction extends AnAction {
652 private final String myUrl;
654 private CopyLinkAction(@NotNull String url) {
655 super("Copy Link Address", null, PlatformIcons.COPY_ICON);
660 public void update(AnActionEvent e) {
661 e.getPresentation().setEnabled(true);
665 public void actionPerformed(AnActionEvent e) {
666 Transferable content = new StringSelection(myUrl);
667 CopyPasteManager.getInstance().setContents(content);
671 private static class OpenLinkInBrowser extends AnAction {
673 private final String myUrl;
675 private OpenLinkInBrowser(@NotNull String url) {
676 super("Open Link in Browser", null, PlatformIcons.WEB_ICON);
681 public void update(AnActionEvent e) {
682 e.getPresentation().setEnabled(true);
686 public void actionPerformed(AnActionEvent e) {
687 BrowserUtil.browse(myUrl);
691 public final static String ELLIPSIS = "...";
692 public static final String ERROR_STR = "www";
693 public static String truncateStringWithEllipsis(final String text, final int maxWidth, final FontMetrics fm) {
694 return truncateStringWithEllipsis(text, maxWidth, new WidthCalculator() {
696 public int stringWidth(String s) {
697 return fm.stringWidth(s);
701 public int charWidth(char c) {
702 return fm.charWidth(c);
707 public interface WidthCalculator {
708 int stringWidth(final String s);
709 int charWidth(final char c);
712 public static String truncateStringWithEllipsis(@NotNull final String text, final int maxWidth, final WidthCalculator fm) {
713 final int error = fm.stringWidth(ERROR_STR);
714 final int wholeWidth = fm.stringWidth(text) + error;
715 if (wholeWidth <= maxWidth || text.isEmpty()) return text;
716 final int ellipsisWidth = fm.stringWidth(ELLIPSIS) + error; // plus some reserve
717 if (ellipsisWidth >= maxWidth) return ELLIPSIS;
719 final int availableWidth = maxWidth - ellipsisWidth;
720 int currentLen = (int)Math.floor(availableWidth / (((double) wholeWidth) / text.length()));
722 final String currentSubstring = text.substring(0, currentLen);
723 int realWidth = fm.stringWidth(currentSubstring);
725 if (realWidth >= availableWidth) {
727 for (int i = currentLen - 1; i >= 0; i--) {
728 if ((realWidth - delta) < availableWidth) return text.substring(0, i) + ELLIPSIS;
729 delta += fm.charWidth(currentSubstring.charAt(i));
731 return text.substring(0, 1) + ELLIPSIS;
734 for (int i = currentLen; i < text.length(); i++) {
735 if ((realWidth + delta) >= availableWidth) return text.substring(0, i) + ELLIPSIS;
736 delta += fm.charWidth(text.charAt(i));
738 return text.substring(0, currentLen) + ELLIPSIS;