| /* |
| * Copyright 2000-2013 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.codeInsight.documentation; |
| |
| import com.intellij.codeInsight.CodeInsightBundle; |
| import com.intellij.codeInsight.hint.ElementLocationUtil; |
| import com.intellij.codeInsight.hint.HintManagerImpl; |
| import com.intellij.codeInsight.hint.HintUtil; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.DataManager; |
| import com.intellij.ide.actions.BaseNavigateToSourceAction; |
| import com.intellij.ide.actions.ExternalJavaDocAction; |
| import com.intellij.lang.documentation.CompositeDocumentationProvider; |
| import com.intellij.lang.documentation.DocumentationProvider; |
| import com.intellij.lang.documentation.ExternalDocumentationHandler; |
| import com.intellij.lang.documentation.ExternalDocumentationProvider; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.actionSystem.impl.ActionButton; |
| import com.intellij.openapi.application.ApplicationBundle; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.editor.colors.EditorColorsManager; |
| import com.intellij.openapi.editor.colors.EditorColorsScheme; |
| import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; |
| import com.intellij.openapi.editor.ex.util.EditorUtil; |
| import com.intellij.openapi.options.FontSize; |
| import com.intellij.openapi.ui.popup.JBPopup; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.registry.Registry; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.wm.ex.WindowManagerEx; |
| import com.intellij.pom.Navigatable; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.SmartPointerManager; |
| import com.intellij.psi.SmartPsiElementPointer; |
| import com.intellij.ui.IdeBorderFactory; |
| import com.intellij.ui.JBColor; |
| import com.intellij.ui.SideBorder; |
| import com.intellij.ui.components.JBLayeredPane; |
| import com.intellij.ui.components.JBScrollPane; |
| import com.intellij.util.Consumer; |
| import com.intellij.util.containers.HashMap; |
| import com.intellij.util.ui.GraphicsUtil; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import javax.swing.event.HyperlinkEvent; |
| import javax.swing.event.HyperlinkListener; |
| import javax.swing.text.*; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.net.URL; |
| import java.util.*; |
| import java.util.List; |
| |
| public class DocumentationComponent extends JPanel implements Disposable, DataProvider { |
| |
| @NonNls private static final String DOCUMENTATION_TOPIC_ID = "reference.toolWindows.Documentation"; |
| |
| private static final DataContext EMPTY_DATA_CONTEXT = new DataContext() { |
| @Override |
| public Object getData(@NonNls String dataId) { |
| return null; |
| } |
| }; |
| |
| private static final int MAX_WIDTH = 500; |
| private static final int MAX_HEIGHT = 300; |
| private static final int MIN_HEIGHT = 45; |
| |
| private DocumentationManager myManager; |
| private SmartPsiElementPointer myElement; |
| |
| private final Stack<Context> myBackStack = new Stack<Context>(); |
| private final Stack<Context> myForwardStack = new Stack<Context>(); |
| private final ActionToolbar myToolBar; |
| private volatile boolean myIsEmpty; |
| private boolean myIsShown; |
| private final JLabel myElementLabel; |
| private Style myFontSizeStyle; |
| private JSlider myFontSizeSlider; |
| private final JComponent mySettingsPanel; |
| private final MyShowSettingsButton myShowSettingsButton; |
| private boolean myIgnoreFontSizeSliderChange; |
| private String myEffectiveExternalUrl; |
| private final MyDictionary<String, Image> myImageProvider = new MyDictionary<String, Image>() { |
| @Override |
| public Image get(Object key) { |
| PsiElement element = getElement(); |
| if (element == null) return null; |
| URL url = (URL)key; |
| Image inMemory = myManager.getElementImage(element, url.toExternalForm()); |
| return inMemory != null ? inMemory : Toolkit.getDefaultToolkit().createImage(url); |
| } |
| }; |
| |
| private static class Context { |
| final SmartPsiElementPointer element; |
| final String text; |
| final Rectangle viewRect; |
| |
| public Context(SmartPsiElementPointer element, String text, Rectangle viewRect) { |
| this.element = element; |
| this.text = text; |
| this.viewRect = viewRect; |
| } |
| } |
| |
| private final JScrollPane myScrollPane; |
| private final JEditorPane myEditorPane; |
| private String myText; // myEditorPane.getText() surprisingly crashes.., let's cache the text |
| private final JPanel myControlPanel; |
| private boolean myControlPanelVisible; |
| private final ExternalDocAction myExternalDocAction; |
| private Consumer<PsiElement> myNavigateCallback; |
| |
| private JBPopup myHint; |
| |
| private final Map<KeyStroke, ActionListener> myKeyboardActions = new HashMap<KeyStroke, ActionListener>(); |
| |
| @Override |
| public boolean requestFocusInWindow() { |
| return myScrollPane.requestFocusInWindow(); |
| } |
| |
| |
| @Override |
| public void requestFocus() { |
| myScrollPane.requestFocus(); |
| } |
| |
| public DocumentationComponent(final DocumentationManager manager, final AnAction[] additionalActions) { |
| myManager = manager; |
| myIsEmpty = true; |
| myIsShown = false; |
| |
| myEditorPane = new JEditorPane(UIUtil.HTML_MIME, "") { |
| @Override |
| public Dimension getPreferredScrollableViewportSize() { |
| if (getWidth() == 0 || getHeight() == 0) { |
| setSize(MAX_WIDTH, MAX_HEIGHT); |
| } |
| Insets ins = myEditorPane.getInsets(); |
| View rootView = myEditorPane.getUI().getRootView(myEditorPane); |
| rootView.setSize(MAX_WIDTH, |
| MAX_HEIGHT); // Necessary! Without this line, size will not increase then you go from small page to bigger one |
| int prefHeight = (int)rootView.getPreferredSpan(View.Y_AXIS); |
| prefHeight += ins.bottom + ins.top + myScrollPane.getHorizontalScrollBar().getMaximumSize().height; |
| return new Dimension(MAX_WIDTH, Math.max(MIN_HEIGHT, Math.min(MAX_HEIGHT, prefHeight))); |
| } |
| |
| { |
| enableEvents(AWTEvent.KEY_EVENT_MASK); |
| } |
| |
| @Override |
| protected void processKeyEvent(KeyEvent e) { |
| KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e); |
| ActionListener listener = myKeyboardActions.get(keyStroke); |
| if (listener != null) { |
| listener.actionPerformed(new ActionEvent(DocumentationComponent.this, 0, "")); |
| e.consume(); |
| return; |
| } |
| super.processKeyEvent(e); |
| } |
| |
| @Override |
| protected void paintComponent(Graphics g) { |
| GraphicsUtil.setupAntialiasing(g); |
| super.paintComponent(g); |
| } |
| |
| @Override |
| public void setDocument(Document doc) { |
| super.setDocument(doc); |
| if (doc instanceof StyledDocument) { |
| doc.putProperty("imageCache", myImageProvider); |
| } |
| } |
| }; |
| DataProvider helpDataProvider = new DataProvider() { |
| @Override |
| public Object getData(@NonNls String dataId) { |
| return PlatformDataKeys.HELP_ID.is(dataId) ? DOCUMENTATION_TOPIC_ID : null; |
| } |
| }; |
| myEditorPane.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, helpDataProvider); |
| myText = ""; |
| myEditorPane.setEditable(false); |
| myEditorPane.setBackground(HintUtil.INFORMATION_COLOR); |
| myEditorPane.setEditorKit(UIUtil.getHTMLEditorKit()); |
| myScrollPane = new JBScrollPane(myEditorPane) { |
| @Override |
| protected void processMouseWheelEvent(MouseWheelEvent e) { |
| if (!EditorSettingsExternalizable.getInstance().isWheelFontChangeEnabled() || !EditorUtil.isChangeFontSize(e)) { |
| super.processMouseWheelEvent(e); |
| return; |
| } |
| |
| int change = Math.abs(e.getWheelRotation()); |
| boolean increase = e.getWheelRotation() <= 0; |
| EditorColorsManager colorsManager = EditorColorsManager.getInstance(); |
| EditorColorsScheme scheme = colorsManager.getGlobalScheme(); |
| FontSize newFontSize = scheme.getQuickDocFontSize(); |
| for (; change > 0; change--) { |
| if (increase) { |
| newFontSize = newFontSize.larger(); |
| } |
| else { |
| newFontSize = newFontSize.smaller(); |
| } |
| } |
| |
| if (newFontSize == scheme.getQuickDocFontSize()) { |
| return; |
| } |
| |
| scheme.setQuickDocFontSize(newFontSize); |
| applyFontSize(); |
| setFontSizeSliderSize(newFontSize); |
| } |
| }; |
| myScrollPane.setBorder(null); |
| myScrollPane.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, helpDataProvider); |
| |
| final MouseAdapter mouseAdapter = new MouseAdapter() { |
| @Override |
| public void mousePressed(MouseEvent e) { |
| myManager.requestFocus(); |
| myShowSettingsButton.hideSettings(); |
| } |
| }; |
| myEditorPane.addMouseListener(mouseAdapter); |
| Disposer.register(this, new Disposable() { |
| @Override |
| public void dispose() { |
| myEditorPane.removeMouseListener(mouseAdapter); |
| } |
| }); |
| |
| final FocusAdapter focusAdapter = new FocusAdapter() { |
| @Override |
| public void focusLost(FocusEvent e) { |
| Component previouslyFocused = WindowManagerEx.getInstanceEx().getFocusedComponent(manager.getProject(getElement())); |
| |
| if (!(previouslyFocused == myEditorPane)) { |
| if (myHint != null && !myHint.isDisposed()) myHint.cancel(); |
| } |
| } |
| }; |
| myEditorPane.addFocusListener(focusAdapter); |
| |
| Disposer.register(this, new Disposable() { |
| @Override |
| public void dispose() { |
| myEditorPane.removeFocusListener(focusAdapter); |
| } |
| }); |
| |
| setLayout(new BorderLayout()); |
| JLayeredPane layeredPane = new JBLayeredPane() { |
| @Override |
| public void doLayout() { |
| final Rectangle r = getBounds(); |
| for (Component component : getComponents()) { |
| if (component instanceof JScrollPane) { |
| component.setBounds(0, 0, r.width, r.height); |
| } |
| else { |
| int insets = 2; |
| Dimension d = component.getPreferredSize(); |
| component.setBounds(r.width - d.width - insets, insets, d.width, d.height); |
| } |
| } |
| } |
| |
| @Override |
| public Dimension getPreferredSize() { |
| Dimension editorPaneSize = myEditorPane.getPreferredScrollableViewportSize(); |
| Dimension controlPanelSize = myControlPanel.getPreferredSize(); |
| return new Dimension(Math.max(editorPaneSize.width, controlPanelSize.width), editorPaneSize.height + controlPanelSize.height); |
| } |
| }; |
| layeredPane.add(myScrollPane); |
| layeredPane.setLayer(myScrollPane, 0); |
| |
| mySettingsPanel = createSettingsPanel(); |
| layeredPane.add(mySettingsPanel); |
| layeredPane.setLayer(mySettingsPanel, JLayeredPane.POPUP_LAYER); |
| add(layeredPane, BorderLayout.CENTER); |
| setOpaque(true); |
| myScrollPane.setViewportBorder(JBScrollPane.createIndentBorder()); |
| |
| final DefaultActionGroup actions = new DefaultActionGroup(); |
| final BackAction back = new BackAction(); |
| final ForwardAction forward = new ForwardAction(); |
| EditDocumentationSourceAction edit = new EditDocumentationSourceAction(); |
| actions.add(back); |
| actions.add(forward); |
| actions.add(myExternalDocAction = new ExternalDocAction()); |
| actions.add(edit); |
| |
| back.registerCustomShortcutSet(CustomShortcutSet.fromString("LEFT"), this); |
| forward.registerCustomShortcutSet(CustomShortcutSet.fromString("RIGHT"), this); |
| myExternalDocAction.registerCustomShortcutSet(CustomShortcutSet.fromString("UP"), this); |
| edit.registerCustomShortcutSet(CommonShortcuts.getEditSource(), this); |
| if (additionalActions != null) { |
| for (final AnAction action : additionalActions) { |
| actions.add(action); |
| ShortcutSet shortcutSet = action.getShortcutSet(); |
| if (shortcutSet != null) { |
| action.registerCustomShortcutSet(shortcutSet, this); |
| } |
| } |
| } |
| |
| myToolBar = ActionManager.getInstance().createActionToolbar(ActionPlaces.JAVADOC_TOOLBAR, actions, true); |
| |
| myControlPanel = new JPanel(); |
| myControlPanel.setLayout(new BorderLayout()); |
| myControlPanel.setBorder(IdeBorderFactory.createBorder(SideBorder.BOTTOM)); |
| JPanel dummyPanel = new JPanel(); |
| |
| myElementLabel = new JLabel(); |
| |
| dummyPanel.setLayout(new BorderLayout()); |
| dummyPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); |
| |
| dummyPanel.add(myElementLabel, BorderLayout.EAST); |
| |
| myControlPanel.add(myToolBar.getComponent(), BorderLayout.WEST); |
| myControlPanel.add(dummyPanel, BorderLayout.CENTER); |
| myControlPanel.add(myShowSettingsButton = new MyShowSettingsButton(), BorderLayout.EAST); |
| myControlPanelVisible = false; |
| |
| final HyperlinkListener hyperlinkListener = new HyperlinkListener() { |
| @Override |
| public void hyperlinkUpdate(HyperlinkEvent e) { |
| HyperlinkEvent.EventType type = e.getEventType(); |
| if (type == HyperlinkEvent.EventType.ACTIVATED) { |
| manager.navigateByLink(DocumentationComponent.this, e.getDescription()); |
| } |
| } |
| }; |
| myEditorPane.addHyperlinkListener(hyperlinkListener); |
| Disposer.register(this, new Disposable() { |
| @Override |
| public void dispose() { |
| myEditorPane.removeHyperlinkListener(hyperlinkListener); |
| } |
| }); |
| |
| registerActions(); |
| |
| updateControlState(); |
| } |
| |
| public DocumentationComponent(final DocumentationManager manager) { |
| this(manager, null); |
| } |
| |
| @Override |
| public Object getData(@NonNls String dataId) { |
| if (DocumentationManager.SELECTED_QUICK_DOC_TEXT.getName().equals(dataId)) { |
| // Javadocs often contain symbols (non-breakable white space). We don't want to copy them as is and replace |
| // with raw white spaces. See IDEA-86633 for more details. |
| String selectedText = myEditorPane.getSelectedText(); |
| return selectedText == null? null : selectedText.replace((char)160, ' '); |
| } |
| |
| return null; |
| } |
| |
| private JComponent createSettingsPanel() { |
| JPanel result = new JPanel(new FlowLayout(FlowLayout.RIGHT, 3, 0)); |
| result.add(new JLabel(ApplicationBundle.message("label.font.size"))); |
| myFontSizeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, FontSize.values().length - 1, 3); |
| myFontSizeSlider.setMinorTickSpacing(1); |
| myFontSizeSlider.setPaintTicks(true); |
| myFontSizeSlider.setPaintTrack(true); |
| myFontSizeSlider.setSnapToTicks(true); |
| UIUtil.setSliderIsFilled(myFontSizeSlider, true); |
| result.add(myFontSizeSlider); |
| result.setBorder(BorderFactory.createLineBorder(JBColor.border(), 1)); |
| |
| myFontSizeSlider.addChangeListener(new ChangeListener() { |
| @Override |
| public void stateChanged(ChangeEvent e) { |
| if (myIgnoreFontSizeSliderChange) { |
| return; |
| } |
| EditorColorsManager colorsManager = EditorColorsManager.getInstance(); |
| EditorColorsScheme scheme = colorsManager.getGlobalScheme(); |
| scheme.setQuickDocFontSize(FontSize.values()[myFontSizeSlider.getValue()]); |
| applyFontSize(); |
| } |
| }); |
| |
| String tooltipText = ApplicationBundle.message("quickdoc.tooltip.font.size.by.wheel"); |
| result.setToolTipText(tooltipText); |
| myFontSizeSlider.setToolTipText(tooltipText); |
| result.setVisible(false); |
| result.setOpaque(true); |
| myFontSizeSlider.setOpaque(true); |
| return result; |
| } |
| |
| private void setFontSizeSliderSize(FontSize fontSize) { |
| myIgnoreFontSizeSliderChange = true; |
| try { |
| FontSize[] sizes = FontSize.values(); |
| for (int i = 0; i < sizes.length; i++) { |
| if (fontSize == sizes[i]) { |
| myFontSizeSlider.setValue(i); |
| break; |
| } |
| } |
| } |
| finally { |
| myIgnoreFontSizeSliderChange = false; |
| } |
| } |
| |
| public boolean isEmpty() { |
| return myIsEmpty; |
| } |
| |
| public void startWait() { |
| myIsEmpty = true; |
| } |
| |
| private void setControlPanelVisible(boolean visible) { |
| if (visible == myControlPanelVisible) return; |
| if (visible) { |
| add(myControlPanel, BorderLayout.NORTH); |
| } |
| else { |
| remove(myControlPanel); |
| } |
| myControlPanelVisible = visible; |
| } |
| |
| public void setHint(JBPopup hint) { |
| myHint = hint; |
| } |
| |
| public JComponent getComponent() { |
| return myEditorPane; |
| } |
| |
| @Nullable |
| public PsiElement getElement() { |
| return myElement != null ? myElement.getElement() : null; |
| } |
| |
| public void setNavigateCallback(Consumer<PsiElement> navigateCallback) { |
| myNavigateCallback = navigateCallback; |
| } |
| |
| public void setText(String text, @Nullable PsiElement element, boolean clearHistory) { |
| setText(text, element, false, clearHistory); |
| } |
| |
| public void setText(String text, PsiElement element, boolean clean, boolean clearHistory) { |
| if (clean && myElement != null) { |
| myBackStack.push(saveContext()); |
| myForwardStack.clear(); |
| } |
| updateControlState(); |
| setData(element, text, clearHistory, null); |
| if (clean) { |
| myIsEmpty = false; |
| } |
| |
| if (clearHistory) clearHistory(); |
| } |
| |
| public void replaceText(String text, PsiElement element) { |
| PsiElement current = getElement(); |
| if (current == null || !current.getManager().areElementsEquivalent(current, element)) return; |
| setText(text, element, false); |
| if (!myBackStack.empty()) myBackStack.pop(); |
| } |
| |
| private void clearHistory() { |
| myForwardStack.clear(); |
| myBackStack.clear(); |
| } |
| |
| public void setData(PsiElement _element, String text, final boolean clearHistory, String effectiveExternalUrl) { |
| myEffectiveExternalUrl = effectiveExternalUrl; |
| if (myElement != null) { |
| myBackStack.push(saveContext()); |
| myForwardStack.clear(); |
| } |
| |
| final SmartPsiElementPointer element = _element != null && _element.isValid() |
| ? SmartPointerManager.getInstance(_element.getProject()).createSmartPsiElementPointer(_element) |
| : null; |
| |
| if (element != null) { |
| myElement = element; |
| } |
| |
| myIsEmpty = false; |
| updateControlState(); |
| setDataInternal(element, text, new Rectangle(0, 0)); |
| |
| if (clearHistory) clearHistory(); |
| } |
| |
| private void setDataInternal(SmartPsiElementPointer element, String text, final Rectangle viewRect) { |
| setDataInternal(element, text, viewRect, false); |
| } |
| |
| private void setDataInternal(SmartPsiElementPointer element, String text, final Rectangle viewRect, boolean skip) { |
| |
| myElement = element; |
| |
| boolean justShown = false; |
| if (!myIsShown && myHint != null) { |
| myEditorPane.setText(text); |
| applyFontSize(); |
| myManager.showHint(myHint); |
| myIsShown = justShown = true; |
| } |
| |
| if (!justShown) { |
| myEditorPane.setText(text); |
| applyFontSize(); |
| } |
| |
| if (!skip) { |
| myText = text; |
| } |
| |
| //noinspection SSBasedInspection |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| myEditorPane.scrollRectToVisible(viewRect); |
| } |
| }); |
| } |
| |
| private void applyFontSize() { |
| Document document = myEditorPane.getDocument(); |
| if (!(document instanceof StyledDocument)) { |
| return; |
| } |
| |
| final StyledDocument styledDocument = (StyledDocument)document; |
| if (myFontSizeStyle == null) { |
| myFontSizeStyle = styledDocument.addStyle("active", null); |
| } |
| |
| EditorColorsManager colorsManager = EditorColorsManager.getInstance(); |
| EditorColorsScheme scheme = colorsManager.getGlobalScheme(); |
| StyleConstants.setFontSize(myFontSizeStyle, scheme.getQuickDocFontSize().getSize()); |
| if (Registry.is("documentation.component.editor.font")) { |
| StyleConstants.setFontFamily(myFontSizeStyle, scheme.getEditorFontName()); |
| } |
| |
| final Style sizeStyle = myFontSizeStyle; |
| ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { |
| @Override |
| public void run() { |
| styledDocument.setCharacterAttributes(0, styledDocument.getLength(), sizeStyle, false); |
| } |
| }); |
| } |
| |
| private void goBack() { |
| if (myBackStack.isEmpty()) return; |
| Context context = myBackStack.pop(); |
| myForwardStack.push(saveContext()); |
| restoreContext(context); |
| updateControlState(); |
| } |
| |
| private void goForward() { |
| if (myForwardStack.isEmpty()) return; |
| Context context = myForwardStack.pop(); |
| myBackStack.push(saveContext()); |
| restoreContext(context); |
| updateControlState(); |
| } |
| |
| private Context saveContext() { |
| Rectangle rect = myScrollPane.getViewport().getViewRect(); |
| return new Context(myElement, myText, rect); |
| } |
| |
| private void restoreContext(Context context) { |
| setDataInternal(context.element, context.text, context.viewRect); |
| if (myNavigateCallback != null) { |
| final PsiElement element = context.element.getElement(); |
| if (element != null) { |
| myNavigateCallback.consume(element); |
| } |
| } |
| } |
| |
| private void updateControlState() { |
| ElementLocationUtil.customizeElementLabel(myElement != null ? myElement.getElement() : null, myElementLabel); |
| myToolBar.updateActionsImmediately(); // update faster |
| setControlPanelVisible(true);//(!myBackStack.isEmpty() || !myForwardStack.isEmpty()); |
| } |
| |
| private class BackAction extends AnAction implements HintManagerImpl.ActionToIgnore { |
| public BackAction() { |
| super(CodeInsightBundle.message("javadoc.action.back"), null, AllIcons.Actions.Back); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| goBack(); |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| Presentation presentation = e.getPresentation(); |
| presentation.setEnabled(!myBackStack.isEmpty()); |
| } |
| } |
| |
| private class ForwardAction extends AnAction implements HintManagerImpl.ActionToIgnore { |
| public ForwardAction() { |
| super(CodeInsightBundle.message("javadoc.action.forward"), null, AllIcons.Actions.Forward); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| goForward(); |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| Presentation presentation = e.getPresentation(); |
| presentation.setEnabled(!myForwardStack.isEmpty()); |
| } |
| } |
| |
| private class EditDocumentationSourceAction extends BaseNavigateToSourceAction { |
| |
| EditDocumentationSourceAction() { |
| super(true); |
| getTemplatePresentation().setIcon(AllIcons.Actions.EditSource); |
| getTemplatePresentation().setText("Edit Source"); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| super.actionPerformed(e); |
| final JBPopup hint = myHint; |
| if (hint != null && hint.isVisible()) { |
| hint.cancel(); |
| } |
| } |
| |
| @Nullable |
| @Override |
| protected Navigatable[] getNavigatables(DataContext dataContext) { |
| SmartPsiElementPointer element = myElement; |
| if (element != null) { |
| PsiElement psiElement = element.getElement(); |
| return psiElement instanceof Navigatable ? new Navigatable[] {(Navigatable)psiElement} : null; |
| } |
| return null; |
| } |
| } |
| |
| |
| private class ExternalDocAction extends AnAction implements HintManagerImpl.ActionToIgnore { |
| public ExternalDocAction() { |
| super(CodeInsightBundle.message("javadoc.action.view.external"), null, AllIcons.Actions.Browser_externalJavaDoc); |
| registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_EXTERNAL_JAVADOC).getShortcutSet(), null); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| if (myElement != null) { |
| final PsiElement element = myElement.getElement(); |
| final DocumentationProvider provider = DocumentationManager.getProviderFromElement(element); |
| final PsiElement originalElement = DocumentationManager.getOriginalElement(element); |
| boolean processed = false; |
| if (provider instanceof CompositeDocumentationProvider) { |
| for (DocumentationProvider p : ((CompositeDocumentationProvider)provider).getAllProviders()) { |
| if (p instanceof ExternalDocumentationHandler && ((ExternalDocumentationHandler)p).handleExternal(element, originalElement)) { |
| processed = true; |
| break; |
| } |
| } |
| } |
| |
| if (!processed) { |
| final Component component = PlatformDataKeys.CONTEXT_COMPONENT.getData(e.getDataContext()); |
| final List<String> urls; |
| if (!StringUtil.isEmptyOrSpaces(myEffectiveExternalUrl)) { |
| urls = Collections.singletonList(myEffectiveExternalUrl); |
| } else { |
| urls = provider.getUrlFor(element, originalElement); |
| assert urls != null : provider; |
| assert !urls.isEmpty() : provider; |
| } |
| ExternalJavaDocAction.showExternalJavadoc(urls, component); |
| } |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| final Presentation presentation = e.getPresentation(); |
| presentation.setEnabled(false); |
| if (myElement != null) { |
| final PsiElement element = myElement.getElement(); |
| final DocumentationProvider provider = DocumentationManager.getProviderFromElement(element); |
| final PsiElement originalElement = DocumentationManager.getOriginalElement(element); |
| if (provider instanceof ExternalDocumentationProvider) { |
| presentation.setEnabled(element != null && ((ExternalDocumentationProvider)provider).hasDocumentationFor(element, originalElement)); |
| } |
| else { |
| final List<String> urls = provider.getUrlFor(element, originalElement); |
| presentation.setEnabled(element != null && urls != null && !urls.isEmpty()); |
| } |
| } |
| } |
| } |
| |
| private void registerActions() { |
| myExternalDocAction |
| .registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_EXTERNAL_JAVADOC).getShortcutSet(), myEditorPane); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); |
| int value = scrollBar.getValue() - scrollBar.getUnitIncrement(-1); |
| value = Math.max(value, 0); |
| scrollBar.setValue(value); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); |
| int value = scrollBar.getValue() + scrollBar.getUnitIncrement(+1); |
| value = Math.min(value, scrollBar.getMaximum()); |
| scrollBar.setValue(value); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getHorizontalScrollBar(); |
| int value = scrollBar.getValue() - scrollBar.getUnitIncrement(-1); |
| value = Math.max(value, 0); |
| scrollBar.setValue(value); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getHorizontalScrollBar(); |
| int value = scrollBar.getValue() + scrollBar.getUnitIncrement(+1); |
| value = Math.min(value, scrollBar.getMaximum()); |
| scrollBar.setValue(value); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); |
| int value = scrollBar.getValue() - scrollBar.getBlockIncrement(-1); |
| value = Math.max(value, 0); |
| scrollBar.setValue(value); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); |
| int value = scrollBar.getValue() + scrollBar.getBlockIncrement(+1); |
| value = Math.min(value, scrollBar.getMaximum()); |
| scrollBar.setValue(value); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getHorizontalScrollBar(); |
| scrollBar.setValue(0); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getHorizontalScrollBar(); |
| scrollBar.setValue(scrollBar.getMaximum()); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); |
| scrollBar.setValue(0); |
| } |
| }); |
| |
| myKeyboardActions.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK), new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| JScrollBar scrollBar = myScrollPane.getVerticalScrollBar(); |
| scrollBar.setValue(scrollBar.getMaximum()); |
| } |
| }); |
| } |
| |
| public String getText() { |
| return myText; |
| } |
| |
| @Override |
| public void dispose() { |
| myBackStack.clear(); |
| myForwardStack.clear(); |
| myKeyboardActions.clear(); |
| myElement = null; |
| myManager = null; |
| myHint = null; |
| myNavigateCallback = null; |
| } |
| |
| private class MyShowSettingsButton extends ActionButton { |
| |
| MyShowSettingsButton() { |
| this(new MyShowSettingsAction(), new Presentation(), ActionPlaces.JAVADOC_INPLACE_SETTINGS, ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE); |
| } |
| |
| MyShowSettingsButton(AnAction action, Presentation presentation, String place, @NotNull Dimension minimumSize) { |
| super(action, presentation, place, minimumSize); |
| myPresentation.setIcon(AllIcons.General.SecondaryGroup); |
| } |
| |
| public void hideSettings() { |
| if (!mySettingsPanel.isVisible()) { |
| return; |
| } |
| AnActionEvent event = new AnActionEvent( |
| null, EMPTY_DATA_CONTEXT, ActionPlaces.JAVADOC_INPLACE_SETTINGS, myPresentation, ActionManager.getInstance(), 0 |
| ); |
| myAction.actionPerformed(event); |
| } |
| } |
| |
| private class MyShowSettingsAction extends ToggleAction { |
| |
| @Override |
| public boolean isSelected(AnActionEvent e) { |
| return mySettingsPanel.isVisible(); |
| } |
| |
| @Override |
| public void setSelected(AnActionEvent e, boolean state) { |
| if (!state) { |
| mySettingsPanel.setVisible(false); |
| return; |
| } |
| |
| EditorColorsManager colorsManager = EditorColorsManager.getInstance(); |
| EditorColorsScheme scheme = colorsManager.getGlobalScheme(); |
| setFontSizeSliderSize(scheme.getQuickDocFontSize()); |
| mySettingsPanel.setVisible(true); |
| } |
| } |
| |
| private static abstract class MyDictionary<K, V> extends Dictionary<K, V> { |
| @Override |
| public int size() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Enumeration<K> keys() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public Enumeration<V> elements() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public V put(K key, V value) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public V remove(Object key) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| } |