| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| 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) { |
| setHistory(textFieldWithHistory, historyProvider.produce(), true); |
| // one-time initialization |
| textFieldWithHistory.removePopupMenuListener(this); |
| } |
| |
| @Override |
| public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
| } |
| |
| @Override |
| public void popupMenuCanceled(PopupMenuEvent e) { |
| } |
| }); |
| } |
| |
| public static void setHistory(@NotNull TextFieldWithHistory textFieldWithHistory, |
| @NotNull List<String> history, |
| boolean mergeWithPrevHistory) { |
| Set<String> newHistorySet = ContainerUtil.newHashSet(history); |
| List<String> prevHistory = textFieldWithHistory.getHistory(); |
| List<String> mergedHistory = ContainerUtil.newArrayList(); |
| if (mergeWithPrevHistory) { |
| for (String item : prevHistory) { |
| if (!newHistorySet.contains(item)) { |
| mergedHistory.add(item); |
| } |
| } |
| } |
| else { |
| String currentText = textFieldWithHistory.getText(); |
| if (StringUtil.isNotEmpty(currentText) && !newHistorySet.contains(currentText)) { |
| mergedHistory.add(currentText); |
| } |
| } |
| mergedHistory.addAll(history); |
| textFieldWithHistory.setHistory(mergedHistory); |
| setLongestAsPrototype(textFieldWithHistory, mergedHistory); |
| } |
| |
| 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, |
| @Nullable Color foregroundColor) { |
| String html = String.format( |
| "<html><head>%s</head><body>%s</body></html>", |
| UIUtil.getCssFontDeclaration(editorPane.getFont(), foregroundColor, null, null), |
| bodyInnerHtml |
| ); |
| editorPane.setText(html); |
| } |
| |
| @NotNull |
| public static TextFieldWithHistoryWithBrowseButton createTextFieldWithHistoryWithBrowseButton(@Nullable Project project, |
| @NotNull String browseDialogTitle, |
| @NotNull FileChooserDescriptor fileChooserDescriptor, |
| @Nullable NotNullProducer<List<String>> historyProvider) { |
| TextFieldWithHistoryWithBrowseButton textFieldWithHistoryWithBrowseButton = new TextFieldWithHistoryWithBrowseButton(); |
| TextFieldWithHistory textFieldWithHistory = textFieldWithHistoryWithBrowseButton.getChildComponent(); |
| textFieldWithHistory.setHistorySize(-1); |
| textFieldWithHistory.setMinimumAndPreferredWidth(0); |
| if (historyProvider != null) { |
| addHistoryOnExpansion(textFieldWithHistory, historyProvider); |
| } |
| installFileCompletionAndBrowseDialog( |
| project, |
| textFieldWithHistoryWithBrowseButton, |
| browseDialogTitle, |
| fileChooserDescriptor |
| ); |
| return textFieldWithHistoryWithBrowseButton; |
| } |
| |
| 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); |
| } |
| } |
| } |