com.android.tools.idea.editors.theme.ThemeEditorTable.java Source code

Java tutorial

Introduction

Here is the source code for com.android.tools.idea.editors.theme.ThemeEditorTable.java

Source

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * 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.android.tools.idea.editors.theme;

import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.resources.ResourceResolver;
import com.android.tools.idea.editors.theme.attributes.AttributesTableModel;
import com.android.tools.idea.editors.theme.attributes.ShowJavadocAction;
import com.android.tools.idea.editors.theme.attributes.TableLabel;
import com.android.tools.idea.editors.theme.attributes.editors.*;
import com.android.tools.idea.editors.theme.datamodels.EditedStyleItem;
import com.android.tools.idea.editors.theme.datamodels.ConfiguredThemeEditorStyle;
import com.android.tools.idea.editors.theme.preview.AndroidThemePreviewPanel;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.JBPopupMenu;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.android.dom.drawable.DrawableDomElement;
import org.jetbrains.android.dom.resources.Flag;
import org.jetbrains.annotations.NotNull;
import spantable.CellSpanModel;
import spantable.CellSpanTable;

import javax.swing.*;
import javax.swing.plaf.ColorUIResource;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.Map;

public class ThemeEditorTable extends CellSpanTable {
    private Map<Class<?>, Integer> myClassHeights;
    private ShowJavadocAction myJavadocAction;
    private ThemeEditorComponent.GoToListener myGoToListener;
    private ThemeEditorContext myContext;

    /**
     * label text consistent with rest of IDE (e.g. right click)
     */
    private static final String GO_TO_DECLARATION = "Go To Declaration";

    public ThemeEditorTable() {
        putClientProperty("terminateEditOnFocusLost", true);
        // We shouldn't allow autoCreateColumnsFromModel, because when setModel() will be invoked, it removes
        // existing listeners to cell editors.
        setAutoCreateColumnsFromModel(false);

        for (int c = 0; c < AttributesTableModel.COL_COUNT; ++c) {
            addColumn(new TableColumn(c));
        }
    }

    @Override
    public int convertRowIndexToModel(int viewRowIndex) {
        // Workaround for http://b.android.com/221881
        // The CellSpanTableUI will incorrectly calculate the row sometimes (and set it to -1) if there is a
        // clipping rect set.
        if (viewRowIndex < 0) {
            viewRowIndex = 0;
        }
        return super.convertRowIndexToModel(viewRowIndex);
    }

    public void setGoToListener(@NotNull ThemeEditorComponent.GoToListener goToListener) {
        myGoToListener = goToListener;
    }

    public void customizeTable(@NotNull ThemeEditorContext context, @NotNull AndroidThemePreviewPanel previewPanel,
            @NotNull ParentRendererEditor.ThemeParentChangedListener themeParentChangedListener) {
        myContext = context;
        myJavadocAction = new ShowJavadocAction(this, myContext);
        setRenderersAndEditors(previewPanel, themeParentChangedListener);
    }

    @Override
    public JPopupMenu getComponentPopupMenu() {
        // Workaround for http://b.android.com/173610
        // getMousePosition returns null sometimes even when the component is visible and the mouse is over the component. This seems to be a
        // bug in LWWindowPeer after a modal dialog has been displayed.
        Point point = MouseInfo.getPointerInfo().getLocation();
        SwingUtilities.convertPointFromScreen(point, this);
        return getPopupMenuAtCell(rowAtPoint(point), columnAtPoint(point));
    }

    @Override
    public void setRowSorter(RowSorter<? extends TableModel> sorter) {
        super.setRowSorter(sorter);
        updateRowHeights();
    }

    public void updateRowHeights() {
        TableModel rawModel = getModel();
        if (!(rawModel instanceof CellSpanModel)) {
            return;
        }

        CellSpanModel myModel = (CellSpanModel) rawModel;

        int defaultRowHeight = myClassHeights.get(Object.class);
        setRowHeight(defaultRowHeight);
        for (int row = 0; row < myModel.getRowCount(); row++) {
            // Find the maximum row height
            int maxRowHeight = -1;
            for (int col = 0; col < myModel.getColumnCount(); col += myModel.getColumnSpan(row, col)) {
                Class<?> cellClass = myModel.getCellClass(row, col);
                Integer rowHeight = myClassHeights.get(cellClass);

                if (rowHeight != null) {
                    maxRowHeight = Math.max(maxRowHeight, rowHeight);
                }
            }

            if (maxRowHeight == -1) {
                // Leave the default size
                continue;
            }
            int viewRow = convertRowIndexToView(row);

            if (viewRow != -1) {
                setRowHeight(viewRow, maxRowHeight);
            }
        }
    }

    public void setClassHeights(Map<Class<?>, Integer> classHeights) {
        myClassHeights = classHeights;
        updateRowHeights();
    }

    private void setRenderersAndEditors(@NotNull AndroidThemePreviewPanel previewPanel,
            @NotNull ParentRendererEditor.ThemeParentChangedListener themeParentChangedListener) {
        Project project = myContext.getProject();
        ResourcesCompletionProvider completionProvider = new ResourcesCompletionProvider(myContext);
        final AttributeReferenceRendererEditor styleEditor = new AttributeReferenceRendererEditor(project,
                completionProvider);

        setDefaultRenderer(Color.class,
                new DelegatingCellRenderer(new ColorRendererEditor(myContext, previewPanel, false)));
        setDefaultRenderer(EditedStyleItem.class,
                new DelegatingCellRenderer(new AttributeReferenceRendererEditor(project, completionProvider)));
        setDefaultRenderer(ConfiguredThemeEditorStyle.class,
                new DelegatingCellRenderer(new AttributeReferenceRendererEditor(project, completionProvider)));
        setDefaultRenderer(String.class, new DelegatingCellRenderer(getDefaultRenderer(String.class)));
        setDefaultRenderer(Integer.class, new DelegatingCellRenderer(new IntegerRenderer()));
        setDefaultRenderer(Boolean.class, new DelegatingCellRenderer(new BooleanRendererEditor(myContext)));
        setDefaultRenderer(Enum.class, new DelegatingCellRenderer(new EnumRendererEditor()));
        setDefaultRenderer(Flag.class, new DelegatingCellRenderer(new FlagRendererEditor()));
        setDefaultRenderer(AttributesTableModel.ParentAttribute.class,
                new DelegatingCellRenderer(new ParentRendererEditor(myContext, themeParentChangedListener)));
        setDefaultRenderer(DrawableDomElement.class,
                new DelegatingCellRenderer(new DrawableRendererEditor(myContext, previewPanel, false)));
        setDefaultRenderer(TableLabel.class, new DefaultTableCellRenderer() {
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                    boolean hasFocus, int row, int column) {
                super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                Font regularFont = UIUtil.getLabelFont();
                int regularFontSize = getFontMetrics(regularFont).getHeight();
                Font headerFont = regularFont
                        .deriveFont(regularFontSize * ThemeEditorConstants.ATTRIBUTES_HEADER_FONT_SCALE);
                this.setFont(headerFont);
                return this;
            }
        });

        setDefaultEditor(Color.class,
                new DelegatingCellEditor(false, new ColorRendererEditor(myContext, previewPanel, true)));
        setDefaultEditor(EditedStyleItem.class,
                new DelegatingCellEditor(false, new AttributeReferenceRendererEditor(project, completionProvider)));
        setDefaultEditor(String.class, new DelegatingCellEditor(false, getDefaultEditor(String.class)));
        setDefaultEditor(Integer.class, new DelegatingCellEditor(getDefaultEditor(Integer.class)));
        setDefaultEditor(Boolean.class, new DelegatingCellEditor(false, new BooleanRendererEditor(myContext)));
        setDefaultEditor(Enum.class, new DelegatingCellEditor(false, new EnumRendererEditor()));
        setDefaultEditor(Flag.class, new DelegatingCellEditor(false, new FlagRendererEditor()));
        setDefaultEditor(AttributesTableModel.ParentAttribute.class,
                new DelegatingCellEditor(false, new ParentRendererEditor(myContext, themeParentChangedListener)));

        // We allow to edit style pointers as Strings.
        setDefaultEditor(ConfiguredThemeEditorStyle.class, new DelegatingCellEditor(false, styleEditor));
        setDefaultEditor(DrawableDomElement.class,
                new DelegatingCellEditor(false, new DrawableRendererEditor(myContext, previewPanel, true)));
    }

    private JPopupMenu getPopupMenuAtCell(final int row, final int column) {
        if (row < 0 || column < 0) {
            return null;
        }

        TableModel rawModel = getModel();
        if (!(rawModel instanceof AttributesTableModel)) {
            return null;
        }

        final AttributesTableModel model = (AttributesTableModel) rawModel;
        AttributesTableModel.RowContents contents = model.getRowContents(this.convertRowIndexToModel(row));

        if (contents instanceof AttributesTableModel.AttributeContents) {
            final AttributesTableModel.AttributeContents attribute = (AttributesTableModel.AttributeContents) contents;

            final EditedStyleItem item = attribute.getValue();
            if (item == null) {
                return null;
            }

            final JBPopupMenu popupMenu = new JBPopupMenu();
            if (attribute.getCellClass(1) == ConfiguredThemeEditorStyle.class) {
                popupMenu.add(new AbstractAction(GO_TO_DECLARATION) {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        myGoToListener.goTo(item);
                    }
                });
            } else {
                final ResourceResolver resolver = myContext.getResourceResolver();
                assert resolver != null;
                final Project project = myContext.getProject();
                final ResourceValue resourceValue = resolver.resolveResValue(item.getSelectedValue());
                final File file = new File(resourceValue.getValue());

                final VirtualFileManager manager = VirtualFileManager.getInstance();
                final VirtualFile virtualFile = file.exists()
                        ? manager.findFileByUrl("file://" + file.getAbsolutePath())
                        : null;
                if (virtualFile != null) {
                    popupMenu.add(new AbstractAction(GO_TO_DECLARATION) {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            final OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile);
                            FileEditorManager.getInstance(project).openEditor(descriptor, true);
                        }
                    });
                }
            }

            myJavadocAction.setCurrentItem(item);
            popupMenu.add(myJavadocAction);

            final ConfiguredThemeEditorStyle selectedStyle = model.getSelectedStyle();
            if (!selectedStyle.isReadOnly() && selectedStyle.hasItem(item)) {
                popupMenu.add(new AbstractAction("Reset value") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        selectedStyle.removeAttribute(item.getQualifiedName());
                        model.fireTableCellUpdated(attribute.getRowIndex(), 0);
                    }
                });
            }

            return popupMenu;
        } else if (contents instanceof AttributesTableModel.ParentAttribute) {
            final ConfiguredThemeEditorStyle parentStyle = model.getSelectedStyle().getParent();
            if (parentStyle == null) {
                return null;
            }

            final JBPopupMenu menu = new JBPopupMenu();
            menu.add(new AbstractAction(GO_TO_DECLARATION) {
                @Override
                public void actionPerformed(ActionEvent e) {
                    myGoToListener.goToParent();
                }
            });

            return menu;
        }

        return null;
    }

    /**
     * Prevents the automatic setting of a background color by the L&F
     */
    @Override
    public void setBackground(Color color) {
        if (color instanceof ColorUIResource) {
            color = null;
        }
        super.setBackground(color);
    }
}