/*
 * Copyright 2003-2016 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 jetbrains.mps.ide.ui.dialogs.properties;

import com.intellij.icons.AllIcons;
import com.intellij.icons.AllIcons.Actions;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.CommonShortcuts;
import com.intellij.openapi.actionSystem.ShortcutSet;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.ui.ComboBoxTableRenderer;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.AnActionButtonRunnable;
import com.intellij.ui.BooleanTableCellRenderer;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.SpeedSearchBase;
import com.intellij.ui.SpeedSearchComparator;
import com.intellij.ui.TabbedPaneWrapper;
import com.intellij.ui.TableUtil;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.table.JBTable;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.util.ui.JBInsets;
import jetbrains.mps.icons.MPSIcons.General;
import jetbrains.mps.ide.ThreadUtils;
import jetbrains.mps.ide.findusages.model.IResultProvider;
import jetbrains.mps.ide.findusages.model.SearchQuery;
import jetbrains.mps.ide.findusages.view.UsageToolOptions;
import jetbrains.mps.ide.findusages.view.UsagesViewTool;
import jetbrains.mps.ide.icons.IdeIcons;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.ide.save.SaveRepositoryCommand;
import jetbrains.mps.ide.ui.dialogs.properties.renders.DependencyCellState;
import jetbrains.mps.ide.ui.dialogs.properties.renders.LanguageTableCellRenderer;
import jetbrains.mps.ide.ui.dialogs.properties.tables.items.DependenciesTableItem;
import jetbrains.mps.ide.ui.dialogs.properties.tables.models.DependTableModel;
import jetbrains.mps.ide.ui.dialogs.properties.tables.models.UsedLangsTableModel;
import jetbrains.mps.ide.ui.dialogs.properties.tables.models.UsedLangsTableModel.Import;
import jetbrains.mps.ide.ui.dialogs.properties.tables.models.UsedLangsTableModel.ValidImportCondition;
import jetbrains.mps.ide.ui.dialogs.properties.tabs.BaseTab;
import jetbrains.mps.project.DevKit;
import jetbrains.mps.project.Project;
import jetbrains.mps.util.IterableUtil;
import jetbrains.mps.util.NotCondition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SLanguage;
import org.jetbrains.mps.openapi.model.SModelReference;
import org.jetbrains.mps.openapi.module.SDependencyScope;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.module.SModuleReference;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.ui.Modifiable;
import org.jetbrains.mps.openapi.ui.persistence.Tab;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * Configuration page consisting of {@link Tab tabs}.
 */
public abstract class MPSPropertiesConfigurable implements Configurable, Disposable {
  private TabbedPaneWrapper myTabbedPaneWrapper = new TabbedPaneWrapper(this);
  private List<Tab> myTabs = new ArrayList<>();
  protected final Project myProject;
  private DialogWrapper myParentForCallBack = null;

  public MPSPropertiesConfigurable(Project project) {
    myProject = project;
  }

  public final void setParentForCallBack(DialogWrapper parentForCallBack) {
    myParentForCallBack = parentForCallBack;
  }

  protected final void forceCancelCloseDialog() {
    if (myParentForCallBack == null) {
      return;
    }
    ThreadUtils.runInUIThreadNoWait(new Runnable() {
      @Override
      public void run() {
        myParentForCallBack.doCancelAction();
      }
    });
  }

  @Override
  public String getHelpTopic() {
    return null;
  }

  @Override
  public void dispose() {
  }

  /**
   * Subclasses may choose whether to instantiate initial tabs on demand with {@link #createInitialTabs()}
   * or register them at once with this method. Supplied tabs would get a chance to build UI
   * at a proper moment some time later (with {@link Modifiable#init()} call.
   * <p>
   * The method may be invoked few times before the UI is created, registered tabs sum up.
   *
   * @param tabs initial set of tabs
   */
  protected void registerTabs(Tab... tabs) {
    myTabs.addAll(Arrays.asList(tabs));
  }

  /**
   * Provides initial set of tabs to appear in the UI. Subclasses may override or could use {@link #registerTabs(Tab...)} instead.
   * Note, tabs supplied would get {@link Modifiable#init() initialized} from EDT thread some moment later, when deemed appropriate
   * (i.e. there's no guarantee all tabs get initialized, chances are implementation may opt to initialize visible tabs only)
   */
  protected Collection<Tab> createInitialTabs() {
    return myTabs;
  }

  @Override
  public final JComponent createComponent() {
    for (Tab tab : createInitialTabs()) {
      tab.init();
      addTab(tab);
    }
    return myTabbedPaneWrapper.getComponent();
  }

  public int getTabsCount() {
    return myTabbedPaneWrapper.getTabCount();
  }

  public final Tab getTab(int index) {
    return myTabs.get(index);
  }

  public void selectTab(Tab tab) {
    selectTab(indexOfTab(tab));
  }

  public void selectTab(int index) {
    myTabbedPaneWrapper.setSelectedIndex(index);
  }

  public void addTab(Tab tab) {
    if (tab == null || tab.getTabComponent() == null) {
      return;
    }
    if (tab.getToolTip() == null && tab instanceof BaseTab) {
      ((BaseTab) tab).setToolTip(tab.getTitle());
    }

    if (!myTabs.contains(tab)) {
      myTabs.add(tab);
    }
    if (myTabbedPaneWrapper.indexOfComponent(tab.getTabComponent()) < 0) {
      myTabbedPaneWrapper.addTab(tab.getTitle(), tab.getIcon(), tab.getTabComponent(), tab.getToolTip());
    }
  }

  public void insertTab(Tab tab, int index) {
    if (tab == null || tab.getTabComponent() == null) {
      return;
    }
    if (tab.getToolTip() == null && tab instanceof BaseTab) {
      ((BaseTab) tab).setToolTip(tab.getTitle());
    }

    if (!myTabs.contains(tab)) {
      myTabs.add(tab);
    }
    if (myTabbedPaneWrapper.indexOfComponent(tab.getTabComponent()) < 0) {
      myTabbedPaneWrapper.insertTab(tab.getTitle(), tab.getIcon(), tab.getTabComponent(), tab.getToolTip(), index);
    }
  }

  private void removeTab(int index) {
    if (index < 0) {
      return;
    }
    myTabbedPaneWrapper.removeTabAt(index);
  }

  protected void removeTab(Tab tab) {
    if (tab == null) {
      return;
    }
    removeTab(myTabbedPaneWrapper.indexOfComponent(tab.getTabComponent()));
    myTabs.remove(tab);
  }

  public int indexOfTab(Tab tab) {
    return myTabbedPaneWrapper.indexOfComponent(tab.getTabComponent());
  }

  public boolean containsTab(Tab tab) {
    return myTabbedPaneWrapper.indexOfComponent(tab.getTabComponent()) >= 0;
  }

  public final void addChangeListener(final ChangeListener listener) {
    if (myTabbedPaneWrapper != null) {
      myTabbedPaneWrapper.addChangeListener(listener);
    }
  }

  @Override
  public void apply() throws ConfigurationException {
    ThreadUtils.assertEDT();
    //see MPS-18743
    new SaveRepositoryCommand(myProject.getRepository()).execute();
    myProject.getModelAccess().executeCommand(new Runnable() {
      @Override
      public void run() {
        for (Tab tab : myTabs) {
          tab.apply();
        }
        save();
      }
    });
  }

  @Override
  public boolean isModified() {
    for (Tab tab : myTabs) {
      if (tab.isModified()) {
        return true;
      }
    }

    return false;
  }

  @Override
  public void reset() {
  }

  @Override
  public void disposeUIResources() {
    Disposer.dispose(this);
  }

  /**
   * If apply method in each tab separately take a lot of time,
   * override this method to perform real save after all applies
   */
  protected void save() {
  }

  /**
   * All modules visible in the current project
   */
  protected final Iterable<SModule> getProjectModules() {
    // wrap into Iterable to ensure lazy construction of module sequence.
    // getModules operation requires read access, but I don't see a reason to
    // move creation of conditional sequence into a read runnable.
    return new Iterable<SModule>() {
      @Override
      public Iterator<SModule> iterator() {
        return myProject.getRepository().getModules().iterator();
      }
    };
  }

  // keep usage view options common to properties page in a single place
  /*package*/ void showUsageImpl(SearchQuery query, IResultProvider provider) {
    final UsageToolOptions uvOpt = new UsageToolOptions().allowRunAgain(true).forceNewTab(true).navigateIfSingle(false).transientView(true);
    UsagesViewTool.showUsages(ProjectHelper.toIdeaProject(myProject), provider, query, uvOpt);
  }


  public abstract class CommonTab extends BaseTab {

    protected JTextField myTextFieldName;

    public CommonTab() {
      super(PropertiesBundle.message("mps.properties.common.title"), AllIcons.General.ProjectSettings, PropertiesBundle.message("mps.properties.common.tip"));
    }

    protected abstract String getConfigItemName();

    protected abstract String getConfigItemPath();

    protected abstract JComponent getBottomComponent();

    protected JComponent getTopComponent() {
      return null;
    }

    @Override
    public void init() {
      JComponent topComponent = getTopComponent();
      int rowCount = 0;

      JPanel sourcesTab = new JPanel();
      sourcesTab.setLayout(new GridLayoutManager(topComponent != null ? 4 : 3, 2, INSETS, -1, -1));

      JBLabel label = new JBLabel(PropertiesBundle.message("mps.properties.common.namelabel"));
      sourcesTab.add(label,
          new GridConstraints(rowCount++, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED,
              GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));

      myTextFieldName = new JTextField();
      myTextFieldName.setText(getConfigItemName());
      sourcesTab.add(myTextFieldName,
          new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW,
              GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));

      label = new JBLabel(PropertiesBundle.message("mps.properties.common.filepathlabel"));
      sourcesTab.add(label,
          new GridConstraints(rowCount, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED,
              GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));

      JTextField textField = new JTextField();
      textField.setEditable(false);
      textField.setText(getConfigItemPath());
      sourcesTab.add(textField,
          new GridConstraints(rowCount++, 1, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW,
              GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));

      if (topComponent != null) {
        sourcesTab.add(topComponent,
            new GridConstraints(rowCount++, 0, 1, 2, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW,
                GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
      }

      sourcesTab.add(getBottomComponent(), new GridConstraints(rowCount, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
          GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
          GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
      setTabComponent(sourcesTab);
    }

    @Override
    public boolean isModified() {
      return !myTextFieldName.getText().equals(getConfigItemName());
    }
  }

  public abstract class DependenciesTab extends BaseTab {

    protected DependTableModel myDependTableModel;

    public DependenciesTab() {
      super(PropertiesBundle.message("mps.properties.dependencies.title"), General.Dependencies, PropertiesBundle.message("mps.properties.dependencies.tip"));
    }

    protected abstract DependTableModel getDependTableModel();

    /*CellEditor for scope cell */
    protected abstract TableCellEditor getTableCellEditor();

    @Override
    public void init() {
      JPanel dependenciesTab = new JPanel();
      dependenciesTab.setLayout(new GridLayoutManager(1, 1, INSETS, -1, -1));

      final JBTable tableDepend = new JBTable();
      tableDepend.setShowHorizontalLines(false);
      tableDepend.setShowVerticalLines(false);
      tableDepend.setAutoCreateRowSorter(false);
      tableDepend.setAutoscrolls(true);
      tableDepend.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

      myDependTableModel = getDependTableModel();
      tableDepend.setModel(myDependTableModel);

      tableDepend.setDefaultRenderer(DependenciesTableItem.class, getTableCellRender());
      tableDepend.setDefaultRenderer(Boolean.class, new BooleanTableCellRenderer());

      tableDepend.setDefaultRenderer(SDependencyScope.class, new ComboBoxTableRenderer<>(SDependencyScope.values()));
      tableDepend.setDefaultEditor(SDependencyScope.class, getTableCellEditor());


      TableColumn column;
      if (myDependTableModel.getExportColumnIndex() >= 0) {
        column = tableDepend.getTableHeader().getColumnModel().getColumn(myDependTableModel.getExportColumnIndex());
        column.setMinWidth(20);
        column.setPreferredWidth(50);
        column.setMaxWidth(50);
      }
      if (myDependTableModel.getRoleColumnIndex() >= 0) {
        column = tableDepend.getTableHeader().getColumnModel().getColumn(myDependTableModel.getRoleColumnIndex());
        column.setMinWidth(80);
        column.setPreferredWidth(130);
        column.setMaxWidth(200);
      }
      if (myDependTableModel.getItemColumnIndex() >= 0) {
        column = tableDepend.getTableHeader().getColumnModel().getColumn(myDependTableModel.getItemColumnIndex());
        column.setPreferredWidth(250);
      }


      ToolbarDecorator decorator = ToolbarDecorator.createDecorator(tableDepend);
      decorator.setAddAction(getAnActionButtonRunnable()).setRemoveAction(new RemoveEntryAction(tableDepend) {
        @Override
        protected boolean confirmRemove(int row) {
          return DependenciesTab.this.confirmRemove(myDependTableModel.getValueAt(row, myDependTableModel.getItemColumnIndex()));
        }
      });
      FindActionButton findActionButton = getFindAnAction(tableDepend);
      if (findActionButton != null) {
        decorator.addExtraAction(findActionButton);
      }

      decorator.setPreferredSize(new Dimension(500, 300));

      JPanel table = decorator.createPanel();
      table.setBorder(IdeBorderFactory.createBorder());
      dependenciesTab.add(table, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
          GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
          GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));

      setTabComponent(dependenciesTab);

      new TableColumnSearch(tableDepend, myDependTableModel.getItemColumnIndex()).setComparator(new SpeedSearchComparator(false, true));
    }

    /*CellRenderer for module column, actual value supplied to renderer is DependenciesTableItem instance */
    protected abstract TableCellRenderer getTableCellRender();

    protected boolean confirmRemove(final Object value) {
      return true;
    }

    @Nullable
    protected FindActionButton getFindAnAction(JBTable table) {
      return null;
    }

    @Override
    public boolean isModified() {
      return myDependTableModel.isModified();
    }

    @Override
    public void apply() {
      myDependTableModel.apply();
    }

    protected abstract AnActionButtonRunnable getAnActionButtonRunnable();
  }

  public abstract class UsedLanguagesTab extends BaseTab {

    protected UsedLangsTableModel myUsedLangsTableModel;
    protected JBTable myUsedLangsTable;

    public UsedLanguagesTab() {
      super(PropertiesBundle.message("mps.properties.usedlanguages.title"), IdeIcons.LANGUAGE_ICON,
          PropertiesBundle.message("mps.properties.usedlanguages.tip"));
    }

    protected abstract UsedLangsTableModel getUsedLangsTableModel();

    protected TableCellRenderer getTableCellRender() {
      SRepository contextRepo = myProject.getRepository();
      LanguageTableCellRenderer tcr = new LanguageTableCellRenderer(contextRepo);
      tcr.addCellState(NotCondition.negate(new ValidImportCondition(contextRepo)), DependencyCellState.NOT_AVAILABLE);
      return tcr;
    }

    @Override
    public void init() {
      JPanel usedLangsTab = new JPanel();
      usedLangsTab.setLayout(new GridLayoutManager(1, 1, INSETS, -1, -1));

      final JBTable usedLangsTable = new JBTable();
      usedLangsTable.setShowHorizontalLines(false);
      usedLangsTable.setShowVerticalLines(false);
      usedLangsTable.setAutoCreateRowSorter(false);
      usedLangsTable.setAutoscrolls(true);
      usedLangsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

      myUsedLangsTableModel = getUsedLangsTableModel();
      usedLangsTable.setModel(myUsedLangsTableModel);
      myUsedLangsTable = usedLangsTable;

      final TableCellRenderer tableCellRender = getTableCellRender();
      usedLangsTable.setDefaultRenderer(UsedLangsTableModel.Import.class, tableCellRender);

      ToolbarDecorator decorator = createToolbar(usedLangsTable);

      decorator.setPreferredSize(new Dimension(500, 300));

      JPanel table = decorator.createPanel();
      table.setBorder(IdeBorderFactory.createBorder());
      usedLangsTab.add(table, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
          GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
          GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));

      setTabComponent(usedLangsTab);

      new TableColumnSearch(usedLangsTable, UsedLangsTableModel.ITEM_COLUMN).setComparator(new SpeedSearchComparator(false, true));
    }

    protected ToolbarDecorator createToolbar(JBTable usedLangsTable) {
      return ToolbarDecorator.createDecorator(usedLangsTable);
    }

    @Override
    public boolean isModified() {
      return myUsedLangsTableModel.isModified();
    }

    protected final List<SLanguage> getSelectedLanguages() {
      final List<SLanguage> languages = new LinkedList<>();
      myProject.getModelAccess().runReadAction(new Runnable() {
        @Override
        public void run() {
          for (int i : myUsedLangsTable.getSelectedRows()) {
            Object value = myUsedLangsTableModel.getValueAt(i, UsedLangsTableModel.ITEM_COLUMN);
            if (value instanceof UsedLangsTableModel.Import) {
              final Import entry = (Import) value;
              if (entry.myLanguage != null) {
                languages.add(entry.myLanguage);
              } else {
                final SModule devkit = entry.myDevKit.resolve(myProject.getRepository());
                if (devkit instanceof DevKit) {
                  languages.addAll(IterableUtil.asCollection(((DevKit) devkit).getAllExportedLanguageIds()));
                }
              }
            }
          }
        }
      });
      return languages;
    }
  }

  public abstract static class FindActionButton extends AnActionButton {
    protected final JBTable myTable;

    public FindActionButton(JBTable table) {
      myTable = table;
      this.getTemplatePresentation().setEnabledAndVisible(true);
      this.getTemplatePresentation().setIcon(Actions.Find);
      this.getTemplatePresentation().setText("Find usages");
    }

    @Override
    public boolean isEnabled() {
      return !(myTable.getSelectionModel().isSelectionEmpty());
    }

    @Override
    public ShortcutSet getShortcut() {
      return CommonShortcuts.getFind();
    }
  }

  /**
   * Search in a single column of a table.
   * <p>
   * To extract text, recognizes SModelReference, SModuleReference as column values, otherwise resort to Object.toString(). Please
   * refactor instead sub-classing if different behavior is desired.
   * <p>
   * Might use com.intellij.ui.TableSpeedSearch instead, if there would be any explanation about what to override there.
   */
  protected static class TableColumnSearch extends SpeedSearchBase<JBTable> {
    private final int myColumnIndex;

    public TableColumnSearch(JBTable table, int columnIndex) {
      super(table);
      myColumnIndex = columnIndex;
    }

    @Override
    public int getSelectedIndex() {
      return myComponent.getSelectedRow();
    }

    @Override
    protected int convertIndexToModel(int viewIndex) {
      return myComponent.convertRowIndexToModel(viewIndex);
    }

    @Override
    public Object[] getAllElements() {
      final TableModel tableModel = myComponent.getModel();
      final int count = tableModel.getRowCount();
      Object[] elements = new Object[count];
      for (int idx = 0; idx < count; idx++) {
        elements[idx] = tableModel.getValueAt(idx, myColumnIndex);
      }
      return elements;
    }

    @Override
    public String getElementText(Object element) {
      if (element instanceof SModuleReference) {
        return ((SModuleReference) element).getModuleName();
      }
      if (element instanceof SModelReference) {
        return ((SModelReference) element).getModelName();
      }
      return String.valueOf(element);
    }

    @Override
    public void selectElement(Object element, String selectedText) {
      final TableModel tableModel = myComponent.getModel();
      final int count = tableModel.getRowCount();
      for (int row = 0; row < count; row++) {
        if (element.equals(tableModel.getValueAt(row, myColumnIndex))) {
          final int viewRow = myComponent.convertRowIndexToView(row);
          myComponent.getSelectionModel().setSelectionInterval(viewRow, viewRow);
          TableUtil.scrollSelectionToVisible(myComponent);
          break;
        }
      }
    }
  }

  protected static class RemoveEntryAction implements AnActionButtonRunnable {
    private final JTable myTable;

    public RemoveEntryAction(@NotNull JTable table) {
      myTable = table;
    }

    @Override
    public void run(AnActionButton anActionButton) {
      int first = myTable.getSelectionModel().getMinSelectionIndex();
      int last = myTable.getSelectionModel().getMaxSelectionIndex();
      for (int i : myTable.getSelectedRows()) {
        if (!confirmRemove(i)) {
          return;
        }
      }
      TableUtil.removeSelectedItems(myTable);
      if (myTable.getModel() instanceof AbstractTableModel) {
        ((AbstractTableModel) myTable.getModel()).fireTableRowsDeleted(first, last);
      }
      first = Math.max(0, first - 1);
      myTable.getSelectionModel().setSelectionInterval(first, first);
    }

    protected boolean confirmRemove(int row) {
      return true;
    }
  }

  public static final JBInsets INSETS = new JBInsets(10, 10, 10, 10);
}
