All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.jumpto.common.ItemRenderer Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.netbeans.modules.jumpto.common;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import javax.swing.ButtonModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.StyleConstants;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.annotations.common.StaticResource;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.settings.FontColorSettings;
import org.netbeans.modules.jumpto.EntityComparator;
import org.netbeans.modules.jumpto.settings.GoToSettings;
import org.openide.awt.HtmlRenderer;
import org.openide.util.ImageUtilities;
import org.openide.util.Parameters;

/**
 *
 * @author Tomas Zezula
 */
public final class ItemRenderer extends DefaultListCellRenderer implements ChangeListener {

    public static interface Convertor {
        String getName(T item);
        String getHighlightText(T item);
        String getOwnerName(T item);
        String getProjectName(T item);
        String getFilePath(T item);
        Icon getItemIcon(T item);
        Icon getProjectIcon(T item);
        boolean isFromCurrentProject(T item);
    }

    public static final class Builder {
        private final JList list;
        private final ButtonModel caseSensitive;
        private final Convertor convertor;
        private String separatorPattern;
        private ButtonModel colorPrefered;
        private ButtonModel searchFolders;

        private Builder(
            @NonNull final JList list,
            @NonNull final ButtonModel caseSensitive,
            @NonNull final Convertor convertor) {
            this.list = list;
            this.caseSensitive = caseSensitive;
            this.convertor = convertor;
        }

        @NonNull
        public ItemRenderer build() {
            return new ItemRenderer(
                    list,
                    caseSensitive,
                    colorPrefered,
                    searchFolders,
                    convertor,
                    separatorPattern);
        }

        @NonNull
        public Builder setCamelCaseSeparator(@NullAllowed final String separatorPattern) {
            this.separatorPattern = separatorPattern;
            return this;
        }

        @NonNull
        public Builder setColorPreferedProject(@NullAllowed final ButtonModel colorPrefered) {
            this.colorPrefered = colorPrefered;
            return this;
        }

        @NonNull
        public Builder setSearchFolders(@NullAllowed final ButtonModel searchFolders) {
            this.searchFolders = searchFolders;
            return this;
        }

        @NonNull
        public static  Builder create(
            @NonNull final JList list,
            @NonNull final ButtonModel caseSensitive,
            @NonNull final Convertor convertor) {
            return new Builder(list, caseSensitive, convertor);
        }
    }

    @StaticResource
    private static final String SAMPLE_ITEM_ICON = "org/netbeans/modules/jumpto/type/sample.png";
    private static final int DARKER_COLOR_COMPONENT = 15;
    private static final int LIGHTER_COLOR_COMPONENT = 80;
    private final GoToSettings.HighlightingMode highlightMode;
    private final HighlightingNameFormatter nameFormater;
    private final String mainProjectName = EntityComparator.getMainProjectName();
    private final Convertor convertor;
    private final MyPanel rendererComponent;
    private final JLabel jlName;
    private final JLabel jlOwner = new JLabel();
    private final JLabel jlPrj = new JLabel();
    private final Color fgColor;
    private final Color fgColorLighter;
    private final Color bgColor;
    private final Color bgColorDarker;
    private final Color bgSelectionColor;
    private final Color fgSelectionColor;
    private final Color bgColorGreener;
    private final Color bgColorDarkerGreener;
    private final JList jList;
    private final ButtonModel caseSensitive;
    private final ButtonModel colorPrefered;
    private final ButtonModel searchFolders;

    private Class clzCache;

    private ItemRenderer(
            @NonNull final JList list,
            @NonNull final ButtonModel caseSensitive,
            @NullAllowed final ButtonModel colorPrefered,
            @NullAllowed final ButtonModel searchFolders,
            @NonNull final Convertor convertor,
            @NullAllowed final String separatorPattern) {
        Parameters.notNull("list", list);   //NOI18N
        Parameters.notNull("caseSensitive", caseSensitive); //NOI18N
        Parameters.notNull("convertor", convertor); //NOI18N
        jList = list;
        this.caseSensitive = caseSensitive;
        this.colorPrefered = colorPrefered;
        this.searchFolders = searchFolders;
        this.convertor = convertor;
        final GoToSettings hs = GoToSettings.getDefault();
        highlightMode = hs.getHighlightingMode();
        nameFormater = createNameFormatter(hs.getHighlightingType(), separatorPattern);
        this.jlName = new JLabel();
        resetNameLabel();
        Container container = list.getParent();
        if ( container instanceof JViewport ) {
            ((JViewport)container).addChangeListener(this);
            stateChanged(new ChangeEvent(container));
        }
        rendererComponent = new MyPanel<>(convertor);
        rendererComponent.setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;
        c.gridwidth = 1;
        c.gridheight = 1;
        c.fill = GridBagConstraints.NONE;
        c.weightx = 0;
        c.anchor = GridBagConstraints.WEST;
        c.insets = new Insets (0,0,0,7);
        rendererComponent.add( jlName, c);
        jlOwner.setOpaque(false);
        jlOwner.setFont(list.getFont());
        c = new GridBagConstraints();
        c.gridx = 1;
        c.gridy = 0;
        c.gridwidth = 1;
        c.gridheight = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 1;
        c.anchor = GridBagConstraints.WEST;
        c.insets = new Insets (0,0,0,7);
        rendererComponent.add( jlOwner, c);

        c = new GridBagConstraints();
        c.gridx = 2;
        c.gridy = 0;
        c.gridwidth = 1;
        c.gridheight = 1;
        c.fill = GridBagConstraints.NONE;
        c.weightx = 0;
        c.anchor = GridBagConstraints.EAST;
        rendererComponent.add( jlPrj, c);


        jlPrj.setOpaque(false);
        jlPrj.setFont(list.getFont());


        jlPrj.setHorizontalAlignment(RIGHT);
        jlPrj.setHorizontalTextPosition(LEFT);

        // setFont( list.getFont() );
        fgColor = list.getForeground();
        fgColorLighter = new Color(
                               Math.min( 255, fgColor.getRed() + LIGHTER_COLOR_COMPONENT),
                               Math.min( 255, fgColor.getGreen() + LIGHTER_COLOR_COMPONENT),
                               Math.min( 255, fgColor.getBlue() + LIGHTER_COLOR_COMPONENT)
                              );

        bgColor = new Color( list.getBackground().getRGB() );
        bgColorDarker = new Color(
                                Math.abs(bgColor.getRed() - DARKER_COLOR_COMPONENT),
                                Math.abs(bgColor.getGreen() - DARKER_COLOR_COMPONENT),
                                Math.abs(bgColor.getBlue() - DARKER_COLOR_COMPONENT)
                        );
        bgSelectionColor = list.getSelectionBackground();
        fgSelectionColor = list.getSelectionForeground();
        bgColorGreener = new Color(
                                    Math.abs(bgColor.getRed() - 20),
                                    Math.min(255, bgColor.getGreen() + 10 ),
                                    Math.abs(bgColor.getBlue() - 20) );


        bgColorDarkerGreener = new Color(
                                Math.abs(bgColorDarker.getRed() - 35),
                                Math.min(255, bgColorDarker.getGreen() + 5 ),
                                Math.abs(bgColorDarker.getBlue() - 35) );
    }

    @NonNull
    @Override
    public Component getListCellRendererComponent(
            @NonNull final JList list,
            @NullAllowed final Object value,
            final int index,
            final boolean isSelected,
            final boolean hasFocus) {

            int height = list.getFixedCellHeight();
            int width = list.getFixedCellWidth() - 1;
            width = width < 200 ? 200 : width;

            Dimension size = new Dimension( width, height );
            rendererComponent.setMaximumSize(size);
            rendererComponent.setPreferredSize(size);
            resetNameLabel();
            if ( isSelected ) {
                jlName.setForeground(fgSelectionColor);
                jlOwner.setForeground(fgSelectionColor);
                jlPrj.setForeground(fgSelectionColor);
                rendererComponent.setBackground(bgSelectionColor);
            } else {
                jlName.setForeground(fgColor);
                jlOwner.setForeground(fgColorLighter);
                jlPrj.setForeground(fgColor);
                rendererComponent.setBackground( index % 2 == 0 ? bgColor : bgColorDarker );
            }

            final T item = dynamic_cast(value);
            if (item != null) {
                jlName.setIcon(convertor.getItemIcon(item));
                jlName.setText(convertor.getName(item));
                jlOwner.setText(convertor.getOwnerName(item));
                if (shouldHighlight(isSelected)) {
                    JLabel highlightedTarget;
                    String textToFormat;
                    if ((searchFolders != null)  && searchFolders.isSelected()) {
                        highlightedTarget = jlOwner;
                        textToFormat = convertor.getOwnerName(item);
                    } else {
                        highlightedTarget = jlName;
                        textToFormat = convertor.getName(item);
                    }
                    String formattedName = highlight(
                            textToFormat,
                            convertor.getHighlightText(item),
                            caseSensitive.isSelected(),
                            isSelected? fgSelectionColor : fgColor);
                    highlightedTarget.setText(formattedName);
                }
                setProjectName(jlPrj, convertor.getProjectName(item));
                jlPrj.setIcon(convertor.getProjectIcon(item));
                if (!isSelected) {
                    final boolean cprj = colorPrefered != null &&
                            colorPrefered.isSelected() &&
                            convertor.isFromCurrentProject(item);
                    final Color bgc =  index % 2 == 0 ?
                        (cprj ? bgColorGreener : bgColor ) :
                        (cprj ? bgColorDarkerGreener : bgColorDarker );
                    jlName.setBackground(bgc);  //Html does not support transparent bg
                    rendererComponent.setBackground(bgc);
                }
		rendererComponent.setItem(item);
            } else {
                jlName.setText(String.valueOf(value));
            }
            return rendererComponent;
        }

    @Override
    public void stateChanged(@NonNull final ChangeEvent event) {
        final JViewport jv = (JViewport)event.getSource();
        jlName.setText( "Sample" ); // NOI18N
        jlName.setIcon(ImageUtilities.loadImageIcon(SAMPLE_ITEM_ICON, false));
        jList.setFixedCellHeight(jlName.getPreferredSize().height);
        jList.setFixedCellWidth(jv.getExtentSize().width);
    }

    @CheckForNull
    @SuppressWarnings("unchecked")
    private T dynamic_cast (
            @NullAllowed final Object obj) {
        if (clzCache == null) {
            for (Type type : convertor.getClass().getGenericInterfaces()) {
                if (type instanceof ParameterizedType && Convertor.class == ((ParameterizedType)type).getRawType()) {
                    clzCache = (Class) ((ParameterizedType)type).getActualTypeArguments()[0];
                    break;
                }
            }
        }
        return clzCache != null && clzCache.isInstance(obj) ?
            clzCache.cast(obj) :
            null;
    }

    private void setProjectName(JLabel jlPrj, String projectName) {
        if(isMainProject(projectName)) {
            jlPrj.setText(getBoldText(projectName));
        } else {
            jlPrj.setText(projectName);
        }
    }

    private String getBoldText(String text) {
        StringBuilder sb = new StringBuilder(""); // NOI18N
        sb.append(text);
        sb.append(""); // NOI18N
        return sb.toString();
    }

    private boolean isMainProject(String projectName) {
        return projectName != null && projectName.equals(mainProjectName) ?
            true : false;
    }

    private static class MyPanel extends JPanel {

        private final Convertor convertor;
	private T item;

        MyPanel(@NonNull final Convertor convertor) {
            this.convertor = convertor;
        }

	void setItem(final  T item) {
	    this.item = item;
	    // since the same component is reused for dirrerent list itens,
	    // null the tool tip
	    putClientProperty(TOOL_TIP_TEXT_KEY, null);
	}

	@Override
	public String getToolTipText() {
	    // the tool tip is gotten from the descriptor
	    // and cached in the standard TOOL_TIP_TEXT_KEY property
	    String text = (String) getClientProperty(TOOL_TIP_TEXT_KEY);
	    if( text == null ) {
                if(this.item != null) {
                    text = convertor.getFilePath(item);
                }
                putClientProperty(TOOL_TIP_TEXT_KEY, text);
	    }
	    return text;
	}
    }

    private void resetNameLabel() {
        final Font font = jList.getFont();
        jlName.setFont(font);
        jlName.setOpaque(false);
    }
    
    private boolean shouldHighlight(final boolean selectedItem) {
        switch (highlightMode) {
            case NONE:
                return false;
            case ACTIVE:
                return selectedItem;
            case ALL:
                return true;
            default:
                throw new IllegalArgumentException(String.valueOf(selectedItem));
        }
    }
    
    @NonNull
    private String highlight(
            @NonNull final String name,
            @NonNull final String highlightText,
            final boolean caseSensitive,
            @NonNull final Color color) {
        return new StringBuilder("").   //NOI18N
                append(nameFormater.formatName(
                        UiUtils.htmlize(name),
                        UiUtils.htmlize(highlightText),
                        caseSensitive,
                        color)).
                append("").  //NOI18N
                toString();
    }
    
    private static HighlightingNameFormatter createNameFormatter(
            @NonNull final GoToSettings.HighlightingType type,
            @NullAllowed final String separatorPattern) {
        switch (type) {
            case BACKGROUND:
                Color back = new Color(236,235,163);
                Color front = Color.BLACK;
                final FontColorSettings colors = MimeLookup.getLookup(MimePath.EMPTY).lookup(FontColorSettings.class);
                if (colors != null) {
                    final AttributeSet attrs = colors.getFontColors("mark-occurrences");  //NOI18N
                    if (attrs != null) {
                        Object o = attrs.getAttribute(StyleConstants.Background);
                        if (o instanceof Color) {
                            back = (Color) o;
                        }
                        o = attrs.getAttribute(StyleConstants.Foreground);
                        if (o instanceof Color) {
                            front = (Color) o;
                        }
                    }
                }
                return HighlightingNameFormatter.Builder.create().
                        setCamelCaseSeparator(separatorPattern).
                        buildColorFormatter(back, front);
            case BOLD:
                return HighlightingNameFormatter.Builder.create().
                    setCamelCaseSeparator(separatorPattern).
                    buildBoldFormatter();
            default:
                throw new IllegalStateException(String.valueOf(type));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy