blob: 5c4e4fa031188c4348fbdc04b1a80f1729dcd222 [file] [log] [blame]
/*
* 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.gradle.editor.ui;
import com.android.tools.idea.gradle.editor.entity.GradleEditorEntity;
import com.android.tools.idea.gradle.editor.metadata.StdGradleEditorEntityMetaData;
import com.intellij.application.options.codeStyle.arrangement.util.IntObjectMap;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.ui.table.JBTable;
import com.intellij.util.ui.AbstractTableCellEditor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class GradleEditorEntityTable extends JBTable implements DataProvider {
private final IntObjectMap<GradleEditorCellComponent> myRendererComponents = new IntObjectMap<GradleEditorCellComponent>();
private final IntObjectMap<GradleEditorCellComponent> myEditorComponents = new IntObjectMap<GradleEditorCellComponent>();
@NotNull private final Project myProject;
private int myEditingRow = -1;
private int myRowUnderMouse = -1;
public GradleEditorEntityTable(@NotNull Project project) {
super(new GradleEditorEntityTableModel());
myProject = project;
setShowColumns(false);
setShowGrid(false);
setRowHeight(32);
setDefaultRenderer(Object.class, new MyRenderer());
getColumnModel().getColumn(0).setCellEditor(new MyEditor());
addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
onMouseMoved(e);
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mouseExited(MouseEvent e) {
onMouseExited();
}
});
getModel().addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) { onTableChange(e); }
});
}
@Override
public void setVisible(boolean aFlag) {
super.setVisible(aFlag);
}
@NotNull
@Override
public GradleEditorEntityTableModel getModel() {
return (GradleEditorEntityTableModel)super.getModel();
}
/**
* Allows to ask for a row where given component was used (if any).
*
* @param component target component
* @return a row where given component was used (if any);
* negative value as an indication that no row for the given component has been found
*/
public int getRowByComponent(@NotNull GradleEditorCellComponent component) {
for (int i = 0, max = getModel().getRowCount(); i < max; i++) {
if (myRendererComponents.get(i) == component || myEditorComponents.get(i) == component) {
return i;
}
}
return -1;
}
private void onTableChange(@NotNull TableModelEvent e) {
final int signum;
switch (e.getType()) {
case TableModelEvent.INSERT:
signum = 1;
break;
case TableModelEvent.DELETE:
signum = -1;
for (int i = e.getLastRow(); i >= e.getFirstRow(); i--) {
myRendererComponents.remove(i);
myEditorComponents.remove(i);
}
break;
default:
return;
}
int shift = Math.abs(e.getFirstRow() - e.getLastRow() + 1) * signum;
myRendererComponents.shiftKeys(e.getFirstRow(), shift);
myEditorComponents.shiftKeys(e.getFirstRow(), shift);
if (myRowUnderMouse >= e.getFirstRow()) {
myRowUnderMouse = -1;
}
if (getModel().getRowCount() > 0) {
repaintRows(0, getModel().getRowCount() - 1, false);
}
}
private void onMouseMoved(@NotNull MouseEvent e) {
int i = rowAtPoint(e.getPoint());
if (i != myRowUnderMouse) {
onMouseExited();
}
if (i < 0) {
return;
}
if (i != myRowUnderMouse) {
onMouseEntered(e);
}
GradleEditorCellComponent component = isEditing() ? myEditorComponents.get(i) : myRendererComponents.get(i);
if (component == null) {
return;
}
Rectangle rectangle = component.onMouseMove(e);
if (rectangle != null) {
repaintScreenBounds(rectangle);
}
}
private void onMouseExited() {
if (myRowUnderMouse < 0) {
return;
}
GradleEditorCellComponent c = isEditing() ? myEditorComponents.get(myRowUnderMouse) : myRendererComponents.get(myRowUnderMouse);
if (c != null) {
c.onMouseExited();
repaintRows(myRowUnderMouse, myRowUnderMouse, false);
}
myRowUnderMouse = -1;
}
private void onMouseEntered(@NotNull MouseEvent e) {
myRowUnderMouse = rowAtPoint(e.getPoint());
GradleEditorCellComponent c = isEditing() ? myEditorComponents.get(myRowUnderMouse) : myRendererComponents.get(myRowUnderMouse);
if (c != null) {
c.onMouseEntered(e);
repaintRows(myRowUnderMouse, myRowUnderMouse, false);
}
}
private void repaintScreenBounds(@NotNull Rectangle bounds) {
Point location = bounds.getLocation();
SwingUtilities.convertPointFromScreen(location, this);
int x = location.x;
int width = bounds.width;
repaint(x, location.y, width, bounds.height);
}
public void repaintRows(int first, int last, boolean rowStructureChanged) {
for (int i = first; i <= last; i++) {
if (rowStructureChanged) {
myRendererComponents.remove(i);
myEditorComponents.remove(i);
}
}
getModel().fireTableRowsUpdated(first, last);
}
@Override
public void removeEditor() {
super.removeEditor();
GradleEditorCellComponent component = myRendererComponents.get(myEditingRow);
if (component != null) {
component.onMouseExited(); // Hide tool bar.
component.getValue(myProject); // Flush changes to the underlying gradle configuration.
}
}
@Nullable
@Override
public Object getData(@NonNls String dataId) {
if (GradleEditorUiConstants.ACTIVE_ENTITY_KEY.is(dataId)) {
if (isEditing()) {
int row = getEditingRow();
if (row >= 0 && row < getRowCount()) {
Object value = getValueAt(row, 0);
return value instanceof GradleEditorEntity ? value : null;
}
}
}
else if (CommonDataKeys.PROJECT.is(dataId)) {
return myProject;
}
return null;
}
@Override
public boolean isCellEditable(int row, int column) {
Object value = getValueAt(row, column);
if (!(value instanceof GradleEditorEntity)) {
return false;
}
GradleEditorEntity entity = (GradleEditorEntity)value;
return !entity.getMetaData().contains(StdGradleEditorEntityMetaData.READ_ONLY)
&& ServiceManager.getService(GradleEditorEntityUiRegistry.class).hasEntityUi(entity);
}
private class MyRenderer implements TableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
GradleEditorCellComponent component = myRendererComponents.get(row);
if (component == null) {
myRendererComponents.set(row, component = new GradleEditorCellComponentImpl(GradleEditorEntityTable.this));
}
return component.bind(table, value, myProject, row, column, false, isSelected, hasFocus);
}
}
private class MyEditor extends AbstractTableCellEditor {
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
myEditingRow = row;
GradleEditorCellComponent component = myEditorComponents.get(row);
if (component == null) {
myEditorComponents.set(row, component = new GradleEditorCellComponentImpl(GradleEditorEntityTable.this));
}
return component.bind(table, value, myProject, row, column, true, isSelected, true);
}
@Nullable
@Override
public Object getCellEditorValue() {
GradleEditorCellComponent component = myEditorComponents.get(myEditingRow);
return component == null ? null : component.getValue(myProject);
}
}
}