com.intellij.ide.bookmarks.Bookmark.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.ide.bookmarks.Bookmark.java

Source

/*
 * 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.ide.bookmarks;

import com.intellij.codeInsight.daemon.GutterMark;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.ide.structureView.StructureViewModel;
import com.intellij.ide.structureView.TreeBasedStructureViewBuilder;
import com.intellij.lang.LanguageStructureViewBuilder;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.ex.MarkupModelEx;
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
import com.intellij.openapi.editor.impl.DocumentMarkupModel;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.editor.markup.HighlighterLayer;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.ui.JBColor;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;

public class Bookmark implements Navigatable {
    private static final JBColor ICON_BACKGROUND_COLOR = new JBColor(new Color(0xffffcc), new Color(0x675133));
    public static final Icon DEFAULT_ICON = new MyDefaultIcon();

    private final VirtualFile myFile;
    @NotNull
    private final OpenFileDescriptor myTarget;
    private final Project myProject;

    private String myDescription;
    private char myMnemonic = 0;
    public static final Font MNEMONIC_FONT = new Font("Monospaced", 0, 11);

    public Bookmark(@NotNull Project project, @NotNull VirtualFile file, int line, @NotNull String description) {
        myFile = file;
        myProject = project;
        myDescription = description;

        myTarget = new OpenFileDescriptor(project, file, line, -1, true);

        addHighlighter();
    }

    public void updateHighlighter() {
        release();
        addHighlighter();
    }

    private void addHighlighter() {
        Document document = FileDocumentManager.getInstance().getCachedDocument(getFile());
        if (document != null) {
            createHighlighter((MarkupModelEx) DocumentMarkupModel.forDocument(document, myProject, true));
        }
    }

    public RangeHighlighter createHighlighter(@NotNull MarkupModelEx markup) {
        final RangeHighlighterEx myHighlighter;
        int line = getLine();
        if (line >= 0) {
            myHighlighter = markup.addPersistentLineHighlighter(line, HighlighterLayer.ERROR + 1, null);
            if (myHighlighter != null) {
                myHighlighter.setGutterIconRenderer(new MyGutterIconRenderer(this));

                TextAttributes textAttributes = EditorColorsManager.getInstance().getGlobalScheme()
                        .getAttributes(CodeInsightColors.BOOKMARKS_ATTRIBUTES);

                Color stripeColor = textAttributes.getErrorStripeColor();
                myHighlighter.setErrorStripeMarkColor(stripeColor != null ? stripeColor : Color.black);
                myHighlighter.setErrorStripeTooltip(getBookmarkTooltip());

                TextAttributes attributes = myHighlighter.getTextAttributes();
                if (attributes == null) {
                    attributes = new TextAttributes();
                }
                attributes.setBackgroundColor(textAttributes.getBackgroundColor());
                attributes.setForegroundColor(textAttributes.getForegroundColor());
                myHighlighter.setTextAttributes(attributes);
            }
        } else {
            myHighlighter = null;
        }
        return myHighlighter;
    }

    public Document getDocument() {
        return FileDocumentManager.getInstance().getDocument(getFile());
    }

    public void release() {
        int line = getLine();
        if (line < 0) {
            return;
        }
        final Document document = getDocument();
        if (document == null)
            return;
        MarkupModelEx markup = (MarkupModelEx) DocumentMarkupModel.forDocument(document, myProject, true);
        final Document markupDocument = markup.getDocument();
        if (markupDocument.getLineCount() <= line)
            return;
        final int startOffset = markupDocument.getLineStartOffset(line);
        final int endOffset = markupDocument.getLineEndOffset(line);

        final Ref<RangeHighlighterEx> found = new Ref<RangeHighlighterEx>();
        markup.processRangeHighlightersOverlappingWith(startOffset, endOffset, new Processor<RangeHighlighterEx>() {
            @Override
            public boolean process(RangeHighlighterEx highlighter) {
                GutterMark renderer = highlighter.getGutterIconRenderer();
                if (renderer instanceof MyGutterIconRenderer
                        && ((MyGutterIconRenderer) renderer).myBookmark == Bookmark.this) {
                    found.set(highlighter);
                    return false;
                }
                return true;
            }
        });
        if (!found.isNull())
            found.get().dispose();
    }

    public Icon getIcon() {
        return myMnemonic == 0 ? DEFAULT_ICON : MnemonicIcon.getIcon(myMnemonic);
    }

    public String getDescription() {
        return myDescription;
    }

    public void setDescription(String description) {
        myDescription = description;
    }

    public char getMnemonic() {
        return myMnemonic;
    }

    public void setMnemonic(char mnemonic) {
        myMnemonic = Character.toUpperCase(mnemonic);
    }

    @NotNull
    public VirtualFile getFile() {
        return myFile;
    }

    @Nullable
    public String getNotEmptyDescription() {
        return StringUtil.isEmpty(myDescription) ? null : myDescription;
    }

    public boolean isValid() {
        if (!getFile().isValid()) {
            return false;
        }

        // There is a possible case that target document line that is referenced by the current bookmark is removed. We assume
        // that corresponding range marker becomes invalid then.
        RangeMarker rangeMarker = myTarget.getRangeMarker();
        return rangeMarker == null || rangeMarker.isValid();
    }

    @Override
    public boolean canNavigate() {
        return myTarget.canNavigate();
    }

    @Override
    public boolean canNavigateToSource() {
        return myTarget.canNavigateToSource();
    }

    @Override
    public void navigate(boolean requestFocus) {
        myTarget.navigate(requestFocus);
    }

    public int getLine() {
        RangeMarker marker = myTarget.getRangeMarker();
        if (marker != null && marker.isValid()) {
            Document document = marker.getDocument();
            return document.getLineNumber(marker.getStartOffset());
        }
        return myTarget.getLine();
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder(getQualifiedName());
        String description = StringUtil.escapeXml(getNotEmptyDescription());
        if (description != null) {
            result.append(": ").append(description);
        }
        return result.toString();
    }

    public String getQualifiedName() {
        String presentableUrl = myFile.getPresentableUrl();
        if (myFile.isDirectory())
            return presentableUrl;

        PsiDocumentManager.getInstance(myProject).commitAllDocuments();
        final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(myFile);

        if (psiFile == null)
            return presentableUrl;

        StructureViewBuilder builder = LanguageStructureViewBuilder.INSTANCE.getStructureViewBuilder(psiFile);
        if (builder instanceof TreeBasedStructureViewBuilder) {
            StructureViewModel model = ((TreeBasedStructureViewBuilder) builder).createStructureViewModel(null);
            Object element;
            try {
                element = model.getCurrentEditorElement();
            } finally {
                model.dispose();
            }
            if (element instanceof NavigationItem) {
                ItemPresentation presentation = ((NavigationItem) element).getPresentation();
                if (presentation != null) {
                    presentableUrl = ((NavigationItem) element).getName() + " " + presentation.getLocationString();
                }
            }
        }

        return IdeBundle.message("bookmark.file.X.line.Y", presentableUrl, getLine() + 1);
    }

    private String getBookmarkTooltip() {
        StringBuilder result = new StringBuilder("Bookmark");
        if (myMnemonic != 0) {
            result.append(" ").append(myMnemonic);
        }
        String description = StringUtil.escapeXml(getNotEmptyDescription());
        if (description != null) {
            result.append(": ").append(description);
        }
        return result.toString();
    }

    static class MnemonicIcon implements Icon {
        private static final MnemonicIcon[] cache = new MnemonicIcon[36];//0..9  + A..Z
        private final char myMnemonic;

        @NotNull
        static MnemonicIcon getIcon(char mnemonic) {
            int index = mnemonic - 48;
            if (index > 9)
                index -= 7;
            if (index < 0 || index > cache.length - 1)
                return new MnemonicIcon(mnemonic);
            if (cache[index] == null)
                cache[index] = new MnemonicIcon(mnemonic);
            return cache[index];
        }

        private MnemonicIcon(char mnemonic) {
            myMnemonic = mnemonic;
        }

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            g.setColor(ICON_BACKGROUND_COLOR);
            g.fillRect(x, y, getIconWidth(), getIconHeight());

            g.setColor(JBColor.GRAY);
            g.drawRect(x, y, getIconWidth(), getIconHeight());

            g.setColor(JBColor.foreground());
            final Font oldFont = g.getFont();
            g.setFont(MNEMONIC_FONT);

            ((Graphics2D) g).drawString(Character.toString(myMnemonic), x + 3, y + getIconHeight() - 1.5F);
            g.setFont(oldFont);
        }

        @Override
        public int getIconWidth() {
            return DEFAULT_ICON.getIconWidth();
        }

        @Override
        public int getIconHeight() {
            return DEFAULT_ICON.getIconHeight();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            MnemonicIcon that = (MnemonicIcon) o;

            return myMnemonic == that.myMnemonic;
        }

        @Override
        public int hashCode() {
            return (int) myMnemonic;
        }
    }

    private static class MyDefaultIcon implements Icon {
        private static final Icon myIcon = AllIcons.Actions.Checked;

        @Override
        public void paintIcon(Component c, Graphics g, int x, int y) {
            Graphics2D g2 = (Graphics2D) g.create();
            try {
                Color gutterBackground = EditorColors.GUTTER_BACKGROUND.getDefaultColor();
                g2.setColor(gutterBackground);
                g2.fillRoundRect(x, y, getIconWidth(), getIconHeight(), 4, 4);
                myIcon.paintIcon(c, g2, x, y);
            } finally {
                g2.dispose();
            }
        }

        @Override
        public int getIconWidth() {
            return myIcon.getIconWidth();
        }

        @Override
        public int getIconHeight() {
            return myIcon.getIconHeight();
        }
    }

    private static class MyGutterIconRenderer extends GutterIconRenderer {
        private final Bookmark myBookmark;

        public MyGutterIconRenderer(@NotNull Bookmark bookmark) {
            myBookmark = bookmark;
        }

        @Override
        @NotNull
        public Icon getIcon() {
            return myBookmark.getIcon();
        }

        @Override
        public String getTooltipText() {
            return myBookmark.getBookmarkTooltip();
        }

        @Override
        public boolean equals(Object obj) {
            return obj instanceof MyGutterIconRenderer
                    && Comparing.equal(getTooltipText(), ((MyGutterIconRenderer) obj).getTooltipText())
                    && Comparing.equal(getIcon(), ((MyGutterIconRenderer) obj).getIcon());
        }

        @Override
        public int hashCode() {
            return getIcon().hashCode();
        }
    }
}