blob: cceed7925c715a9e40bee6cddc11cbea3291bb75 [file] [log] [blame]
/*
* Copyright 2002-2005 Sascha Weinreuter
*
* 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 org.intellij.plugins.xpathView;
import com.intellij.codeInsight.hint.HintManagerImpl;
import com.intellij.codeInsight.hint.HintUtil;
import com.intellij.codeInsight.template.impl.DefaultLiveTemplatesProvider;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizable;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.ui.Gray;
import com.intellij.ui.JBColor;
import com.intellij.ui.LightweightHint;
import org.intellij.plugins.xpathView.util.HighlighterUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.*;
import java.util.List;
/**
* Application component.<br>
* This component holds the application-level configuration and registers an own handler for
* ESC-Action to clear highlighters.<br>
* <p/>
* Also used to manage highlighters.
*/
public class XPathAppComponent implements ApplicationComponent, JDOMExternalizable, DefaultLiveTemplatesProvider {
private static final String ACTION_FIND_NEXT = "FindNext";
private static final String ACTION_FIND_PREVIOUS = "FindPrevious";
private AnAction nextAction;
private AnAction prevAction;
private Config configuration = new Config();
@NotNull
public String getComponentName() {
return "XPathView.XPathViewPlugin";
}
public void initComponent() {
final ActionManager actionManager = ActionManager.getInstance();
nextAction = actionManager.getAction(ACTION_FIND_NEXT);
prevAction = actionManager.getAction(ACTION_FIND_PREVIOUS);
actionManager.unregisterAction(ACTION_FIND_NEXT);
actionManager.unregisterAction(ACTION_FIND_PREVIOUS);
actionManager.registerAction(ACTION_FIND_NEXT, new MyFindAction(nextAction, false));
actionManager.registerAction(ACTION_FIND_PREVIOUS, new MyFindAction(prevAction, true));
}
public void disposeComponent() {
// IDEA-97697
// final ActionManager actionManager = ActionManager.getInstance();
// actionManager.unregisterAction(ACTION_FIND_NEXT);
// actionManager.unregisterAction(ACTION_FIND_PREVIOUS);
// actionManager.registerAction(ACTION_FIND_NEXT, nextAction);
// actionManager.registerAction(ACTION_FIND_PREVIOUS, prevAction);
}
public void readExternal(Element element) throws InvalidDataException {
configuration.readExternal(element);
}
public void writeExternal(Element element) throws WriteExternalException {
configuration.writeExternal(element);
}
/**
* Returns the configuration of this plugin
*
* @return the configuration object
* @see Config
*/
@NotNull
public Config getConfig() {
return configuration;
}
public void setConfig(@NotNull Config configuration) {
this.configuration = configuration;
}
public static XPathAppComponent getInstance() {
return ApplicationManager.getApplication().getComponent(XPathAppComponent.class);
}
class MyFindAction extends AnAction implements DumbAware {
private final AnAction origAction;
private final boolean isPrev;
private boolean wrapAround;
public MyFindAction(AnAction origAction, boolean isPrev) {
this.origAction = origAction;
this.isPrev = isPrev;
copyFrom(origAction);
setEnabledInModalContext(origAction.isEnabledInModalContext());
}
public void actionPerformed(AnActionEvent event) {
final Editor editor = LangDataKeys.EDITOR.getData(event.getDataContext());
if (editor != null) {
if (HighlighterUtil.hasHighlighters(editor)) {
final int offset = editor.getCaretModel().getOffset();
final List<RangeHighlighter> hl = HighlighterUtil.getHighlighters(editor);
int diff = Integer.MAX_VALUE;
RangeHighlighter next = null;
for (RangeHighlighter highlighter : hl) {
if (isPrev) {
if (highlighter.getStartOffset() < offset && offset - highlighter.getStartOffset() < diff) {
diff = offset - highlighter.getStartOffset();
next = highlighter;
}
} else {
if (highlighter.getStartOffset() > offset && highlighter.getStartOffset() - offset < diff) {
diff = highlighter.getStartOffset() - offset;
next = highlighter;
}
}
}
final int startOffset;
if (next != null) {
startOffset = next.getStartOffset();
} else if (wrapAround) {
startOffset = hl.get(isPrev ? hl.size() - 1 : 0).getStartOffset();
} else {
final String info = (isPrev ? "First" : "Last") +
" XPath match reached. Press " +
(isPrev ? getShortcutText(prevAction) : getShortcutText(nextAction)) +
" to search from the " +
(isPrev ? "bottom" : "top");
showEditorHint(info, editor);
wrapAround = true;
return;
}
editor.getScrollingModel().scrollTo(editor.offsetToLogicalPosition(startOffset), ScrollType.MAKE_VISIBLE);
editor.getCaretModel().moveToOffset(startOffset);
wrapAround = false;
return;
}
}
origAction.actionPerformed(event);
}
public void update(AnActionEvent event) {
super.update(event);
origAction.update(event);
}
public boolean displayTextInToolbar() {
return origAction.displayTextInToolbar();
}
public void setDefaultIcon(boolean b) {
origAction.setDefaultIcon(b);
}
public boolean isDefaultIcon() {
return origAction.isDefaultIcon();
}
}
public static void showEditorHint(final String info, final Editor editor) {
final JLabel label = new JLabel(info);
label.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.WHITE, Gray._128),
BorderFactory.createEmptyBorder(3, 5, 3, 5)));
label.setForeground(JBColor.foreground());
label.setBackground(HintUtil.INFORMATION_COLOR);
label.setOpaque(true);
label.setFont(label.getFont().deriveFont(Font.BOLD));
final LightweightHint h = new LightweightHint(label);
final Point point = editor.visualPositionToXY(editor.getCaretModel().getVisualPosition());
SwingUtilities.convertPointToScreen(point, editor.getContentComponent());
/* === HintManager API Info ===
public void showEditorHint(final LightweightHint hint,
final Editor editor,
Point p,
int flags,
int timeout,
boolean reviveOnEditorChange)
reviveOnEditorChange means hint should stay even if active editor have been changed. It's should rarely be true.
possible flags are:
public static final int HIDE_BY_ESCAPE = 0x01;
public static final int HIDE_BY_ANY_KEY = 0x02;
public static final int HIDE_BY_LOOKUP_ITEM_CHANGE = 0x04;
public static final int HIDE_BY_TEXT_CHANGE = 0x08;
public static final int HIDE_BY_OTHER_HINT = 0x10;
public static final int HIDE_BY_SCROLLING = 0x20;
public static final int HIDE_IF_OUT_OF_EDITOR = 0x40;
public static final int UPDATE_BY_SCROLLING = 0x80;
*/
final int flags = HintManagerImpl.HIDE_BY_ANY_KEY | HintManagerImpl.HIDE_BY_SCROLLING;
HintManagerImpl.getInstanceImpl().showEditorHint(h, editor, point, flags, 0, false);
}
public static String getShortcutText(final String actionId) {
return getShortcutText(ActionManager.getInstance().getAction(actionId));
}
public static String getShortcutText(final AnAction action) {
final ShortcutSet shortcutSet = action.getShortcutSet();
final Shortcut[] shortcuts = shortcutSet.getShortcuts();
for (final Shortcut shortcut : shortcuts) {
final String text = KeymapUtil.getShortcutText(shortcut);
if (text.length() > 0) return text;
}
return ActionManager.getInstance().getId(action);
}
public String[] getDefaultLiveTemplateFiles() {
return new String[] {"/liveTemplates/xsl"};
}
@Override
public String[] getHiddenLiveTemplateFiles() {
return null;
}
}