diff options
Diffstat (limited to 'platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java')
-rw-r--r-- | platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java new file mode 100644 index 000000000000..ac4c1b12373c --- /dev/null +++ b/platform/platform-impl/src/com/intellij/util/ui/SwingHelper.java @@ -0,0 +1,465 @@ +package com.intellij.util.ui; + +import com.intellij.ide.BrowserUtil; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserFactory; +import com.intellij.openapi.ide.CopyPasteManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ComponentWithBrowseButton; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.TextComponentAccessor; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.HyperlinkLabel; +import com.intellij.ui.TextFieldWithHistory; +import com.intellij.ui.TextFieldWithHistoryWithBrowseButton; +import com.intellij.util.NotNullProducer; +import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.ComparatorUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import java.awt.*; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public class SwingHelper { + + private static final Logger LOG = Logger.getInstance(SwingHelper.class); + + /** + * Creates panel whose content consists of given {@code children} components + * stacked vertically each on another in a given order. + * + * @param childAlignmentX Component.LEFT_ALIGNMENT, Component.CENTER_ALIGNMENT or Component.RIGHT_ALIGNMENT + * @param children children components + * @return created panel + */ + @NotNull + public static JPanel newVerticalPanel(float childAlignmentX, Component... children) { + return newGenericBoxPanel(true, childAlignmentX, children); + } + + @NotNull + public static JPanel newLeftAlignedVerticalPanel(Component... children) { + return newVerticalPanel(Component.LEFT_ALIGNMENT, children); + } + + @NotNull + public static JPanel newLeftAlignedVerticalPanel(@NotNull Collection<Component> children) { + return newVerticalPanel(Component.LEFT_ALIGNMENT, children); + } + + @NotNull + public static JPanel newVerticalPanel(float childAlignmentX, @NotNull Collection<Component> children) { + return newVerticalPanel(childAlignmentX, children.toArray(new Component[children.size()])); + } + + /** + * Creates panel whose content consists of given {@code children} components horizontally + * stacked each on another in a given order. + * + * @param childAlignmentY Component.TOP_ALIGNMENT, Component.CENTER_ALIGNMENT or Component.BOTTOM_ALIGNMENT + * @param children children components + * @return created panel + */ + @NotNull + public static JPanel newHorizontalPanel(float childAlignmentY, Component... children) { + return newGenericBoxPanel(false, childAlignmentY, children); + } + + @NotNull + public static JPanel newHorizontalPanel(float childAlignmentY, @NotNull Collection<Component> children) { + return newHorizontalPanel(childAlignmentY, children.toArray(new Component[children.size()])); + } + + private static JPanel newGenericBoxPanel(boolean verticalOrientation, + float childAlignment, + Component... children) { + JPanel panel = new JPanel(); + int axis = verticalOrientation ? BoxLayout.Y_AXIS : BoxLayout.X_AXIS; + panel.setLayout(new BoxLayout(panel, axis)); + for (Component child : children) { + panel.add(child, childAlignment); + if (child instanceof JComponent) { + JComponent jChild = (JComponent) child; + if (verticalOrientation) { + jChild.setAlignmentX(childAlignment); + } else { + jChild.setAlignmentY(childAlignment); + } + } + } + return panel; + } + + @NotNull + public static JPanel wrapWithoutStretch(@NotNull JComponent component) { + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + panel.add(component); + return panel; + } + + @NotNull + public static JPanel wrapWithHorizontalStretch(@NotNull JComponent component) { + JPanel panel = new JPanel(new BorderLayout(0, 0)); + panel.add(component, BorderLayout.NORTH); + return panel; + } + + public static void setPreferredWidthToFitText(@NotNull TextFieldWithHistoryWithBrowseButton component) { + int childWidth = calcWidthToFitText(component.getChildComponent().getTextEditor(), 35); + setPreferredWidthForComponentWithBrowseButton(component, childWidth); + } + + public static void setPreferredWidthToFitText(@NotNull TextFieldWithBrowseButton component) { + int childWidth = calcWidthToFitText(component.getChildComponent(), 20); + setPreferredWidthForComponentWithBrowseButton(component, childWidth); + } + + private static <T extends JComponent> void setPreferredWidthForComponentWithBrowseButton(@NotNull ComponentWithBrowseButton<T> component, + int childPrefWidth) { + Dimension buttonPrefSize = component.getButton().getPreferredSize(); + setPreferredWidth(component, childPrefWidth + buttonPrefSize.width); + } + + public static void setPreferredWidthToFitText(@NotNull JTextField textField) { + setPreferredWidthToFitText(textField, 15); + } + + public static void setPreferredWidthToFitText(@NotNull JTextField textField, int additionalWidth) { + setPreferredSizeToFitText(textField, StringUtil.notNullize(textField.getText()), additionalWidth); + } + + public static void setPreferredWidthToFitText(@NotNull JTextField textField, @NotNull String text) { + setPreferredSizeToFitText(textField, text, 15); + } + + private static void setPreferredSizeToFitText(@NotNull JTextField textField, @NotNull String text, int additionalWidth) { + int width = calcWidthToFitText(textField, text, additionalWidth); + setPreferredWidth(textField, width); + } + + private static int calcWidthToFitText(@NotNull JTextField textField, int additionalWidth) { + return calcWidthToFitText(textField, textField.getText(), additionalWidth); + } + + private static int calcWidthToFitText(@NotNull JTextField textField, @NotNull String text, int additionalWidth) { + return textField.getFontMetrics(textField.getFont()).stringWidth(text) + additionalWidth; + } + + public static void adjustDialogSizeToFitPreferredSize(@NotNull DialogWrapper dialogWrapper) { + JRootPane rootPane = dialogWrapper.getRootPane(); + Dimension componentSize = rootPane.getSize(); + Dimension componentPreferredSize = rootPane.getPreferredSize(); + if (componentPreferredSize.width <= componentSize.width && componentPreferredSize.height <= componentSize.height) { + return; + } + int dw = Math.max(0, componentPreferredSize.width - componentSize.width); + int dh = Math.max(0, componentPreferredSize.height - componentSize.height); + + Dimension oldDialogSize = dialogWrapper.getSize(); + Dimension newDialogSize = new Dimension(oldDialogSize.width + dw, oldDialogSize.height + dh); + + dialogWrapper.setSize(newDialogSize.width, newDialogSize.height); + rootPane.revalidate(); + rootPane.repaint(); + + LOG.info("DialogWrapper '" + dialogWrapper.getTitle() + "' has been resized (added width: " + dw + ", added height: " + dh + ")"); + } + + public static <T> void updateItems(@NotNull JComboBox comboBox, + @NotNull List<T> newItems, + @Nullable T newSelectedItemIfSelectionCannotBePreserved) { + if (!shouldUpdate(comboBox, newItems)) { + return; + } + Object selectedItem = comboBox.getSelectedItem(); + //noinspection SuspiciousMethodCalls + if (selectedItem != null && !newItems.contains(selectedItem)) { + selectedItem = null; + } + if (selectedItem == null && newItems.contains(newSelectedItemIfSelectionCannotBePreserved)) { + selectedItem = newSelectedItemIfSelectionCannotBePreserved; + } + comboBox.removeAllItems(); + for (T newItem : newItems) { + comboBox.addItem(newItem); + } + if (selectedItem != null) { + int count = comboBox.getItemCount(); + for (int i = 0; i < count; i++) { + Object item = comboBox.getItemAt(i); + if (selectedItem.equals(item)) { + comboBox.setSelectedIndex(i); + break; + } + } + } + } + + private static <T> boolean shouldUpdate(@NotNull JComboBox comboBox, @NotNull List<T> newItems) { + int count = comboBox.getItemCount(); + if (newItems.size() != count) { + return true; + } + for (int i = 0; i < count; i++) { + Object oldItem = comboBox.getItemAt(i); + T newItem = newItems.get(i); + if (!ComparatorUtil.equalsNullable(oldItem, newItem)) { + return true; + } + } + return false; + } + + public static void setNoBorderCellRendererFor(@NotNull TableColumn column) { + final TableCellRenderer previous = column.getCellRenderer(); + column.setCellRenderer(new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + Component component; + if (previous != null) { + component = previous.getTableCellRendererComponent(table, value, isSelected, false, row, column); + } + else { + component = super.getTableCellRendererComponent(table, value, isSelected, false, row, column); + } + if (component instanceof JComponent) { + ((JComponent)component).setBorder(null); + } + return component; + } + }); + } + + public static void addHistoryOnExpansion(@NotNull final TextFieldWithHistory textFieldWithHistory, + @NotNull final NotNullProducer<List<String>> historyProvider) { + textFieldWithHistory.addPopupMenuListener(new PopupMenuListener() { + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + List<String> newHistory = historyProvider.produce(); + Set<String> newHistorySet = ContainerUtil.newHashSet(newHistory); + List<String> oldHistory = textFieldWithHistory.getHistory(); + List<String> mergedHistory = ContainerUtil.newArrayList(); + for (String item : oldHistory) { + if (!newHistorySet.contains(item)) { + mergedHistory.add(item); + } + } + mergedHistory.addAll(newHistory); + textFieldWithHistory.setHistory(mergedHistory); + + setLongestAsPrototype(textFieldWithHistory, mergedHistory); + + // one-time initialization + textFieldWithHistory.removePopupMenuListener(this); + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + } + + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + } + }); + } + + private static void setLongestAsPrototype(@NotNull JComboBox comboBox, @NotNull List<String> variants) { + Object prototypeDisplayValue = comboBox.getPrototypeDisplayValue(); + String prototypeDisplayValueStr = null; + if (prototypeDisplayValue instanceof String) { + prototypeDisplayValueStr = (String) prototypeDisplayValue; + } + else if (prototypeDisplayValue != null) { + return; + } + String longest = StringUtil.notNullize(prototypeDisplayValueStr); + boolean updated = false; + for (String s : variants) { + if (longest.length() < s.length()) { + longest = s; + updated = true; + } + } + if (updated) { + comboBox.setPrototypeDisplayValue(longest); + } + } + + public static void installFileCompletionAndBrowseDialog(@Nullable Project project, + @NotNull TextFieldWithHistoryWithBrowseButton textFieldWithHistoryWithBrowseButton, + @NotNull String browseDialogTitle, + @NotNull FileChooserDescriptor fileChooserDescriptor) { + doInstall(project, + textFieldWithHistoryWithBrowseButton, + textFieldWithHistoryWithBrowseButton.getChildComponent().getTextEditor(), + browseDialogTitle, + fileChooserDescriptor, + TextComponentAccessor.TEXT_FIELD_WITH_HISTORY_WHOLE_TEXT); + } + + public static void installFileCompletionAndBrowseDialog(@Nullable Project project, + @NotNull TextFieldWithBrowseButton textFieldWithBrowseButton, + @NotNull String browseDialogTitle, + @NotNull FileChooserDescriptor fileChooserDescriptor) { + doInstall(project, + textFieldWithBrowseButton, + textFieldWithBrowseButton.getTextField(), + browseDialogTitle, + fileChooserDescriptor, + TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT); + } + + private static <T extends JComponent> void doInstall(@Nullable Project project, + @NotNull ComponentWithBrowseButton<T> componentWithBrowseButton, + @NotNull JTextField textField, + @NotNull String browseDialogTitle, + @NotNull FileChooserDescriptor fileChooserDescriptor, + @NotNull TextComponentAccessor<T> textComponentAccessor) { + fileChooserDescriptor = fileChooserDescriptor.withShowHiddenFiles(SystemInfo.isUnix); + componentWithBrowseButton.addBrowseFolderListener( + project, + new ComponentWithBrowseButton.BrowseFolderActionListener<T>( + browseDialogTitle, + null, + componentWithBrowseButton, + project, + fileChooserDescriptor, + textComponentAccessor + ), + true + ); + FileChooserFactory.getInstance().installFileCompletion( + textField, + fileChooserDescriptor, + true, + project + ); + } + + @NotNull + public static HyperlinkLabel createWebHyperlink(@NotNull String url) { + return createWebHyperlink(url, url); + } + + @NotNull + public static HyperlinkLabel createWebHyperlink(@NotNull String text, @NotNull String url) { + HyperlinkLabel hyperlink = new HyperlinkLabel(text); + hyperlink.setHyperlinkTarget(url); + + DefaultActionGroup actionGroup = new DefaultActionGroup(); + actionGroup.add(new OpenLinkInBrowser(url)); + actionGroup.add(new CopyLinkAction(url)); + + hyperlink.setComponentPopupMenu(ActionManager.getInstance().createActionPopupMenu("web hyperlink", actionGroup).getComponent()); + return hyperlink; + } + + public static void setPreferredWidth(@NotNull JComponent component, int width) { + Dimension preferredSize = component.getPreferredSize(); + preferredSize.width = width; + component.setPreferredSize(preferredSize); + } + + @NotNull + public static JEditorPane createHtmlViewer(boolean carryTextOver, + @Nullable Font font, + @Nullable Color background, + @Nullable Color foreground) { + final JEditorPane textPane; + if (carryTextOver) { + textPane = new JEditorPane() { + @Override + public Dimension getPreferredSize() { + // This trick makes text component to carry text over to the next line + // if the text line width exceeds parent's width + Dimension dimension = super.getPreferredSize(); + dimension.width = 0; + return dimension; + } + }; + } + else { + textPane = new JEditorPane(); + } + textPane.setFont(font != null ? font : UIUtil.getLabelFont()); + textPane.setContentType(UIUtil.HTML_MIME); + textPane.setEditable(false); + textPane.setBackground(background != null ? background : UIUtil.getLabelBackground()); + textPane.setForeground(foreground != null ? foreground : UIUtil.getLabelForeground()); + return textPane; + } + + public static void setHtml(@NotNull JEditorPane editorPane, @NotNull String bodyInnerHtml) { + String html = String.format( + "<html><head>%s</head><body>%s</body></html>", + UIUtil.getCssFontDeclaration(editorPane.getFont(), null, null, null), + bodyInnerHtml + ); + editorPane.setText(html); + } + + private static class CopyLinkAction extends AnAction { + + private final String myUrl; + + private CopyLinkAction(@NotNull String url) { + super("Copy Link Address", null, PlatformIcons.COPY_ICON); + myUrl = url; + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(true); + } + + @Override + public void actionPerformed(AnActionEvent e) { + Transferable content = new StringSelection(myUrl); + CopyPasteManager.getInstance().setContents(content); + } + } + + private static class OpenLinkInBrowser extends AnAction { + + private final String myUrl; + + private OpenLinkInBrowser(@NotNull String url) { + super("Open Link in Browser", null, PlatformIcons.WEB_ICON); + myUrl = url; + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(true); + } + + @Override + public void actionPerformed(AnActionEvent e) { + BrowserUtil.browse(myUrl); + } + } +}
\ No newline at end of file |