<commit_before>// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package git4idea.rebase.interactive.dialog

import com.intellij.icons.AllIcons
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.keymap.KeymapUtil
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.ui.CommitIconTableCellRenderer
import com.intellij.ui.ColoredTableCellRenderer
import com.intellij.ui.JBColor
import com.intellij.ui.SimpleTextAttributes
import com.intellij.ui.TableSpeedSearch
import com.intellij.ui.scale.JBUIScale
import com.intellij.ui.speedSearch.SpeedSearchUtil
import com.intellij.ui.table.JBTable
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.intellij.vcs.log.data.index.IndexedDetails
import com.intellij.vcs.log.paint.PaintParameters
import git4idea.rebase.interactive.GitRebaseTodoModel
import git4idea.rebase.interactive.dialog.GitRebaseCommitsTableView.Companion.DEFAULT_CELL_HEIGHT
import git4idea.rebase.interactive.dialog.GitRebaseCommitsTableView.Companion.GRAPH_COLOR
import git4idea.rebase.interactive.dialog.GitRebaseCommitsTableView.Companion.GRAPH_LINE_WIDTH
import git4idea.rebase.interactive.dialog.GitRebaseCommitsTableView.Companion.GRAPH_NODE_WIDTH
import git4idea.rebase.interactive.dialog.view.CommitMessageCellEditor
import java.awt.*
import javax.swing.DefaultListSelectionModel
import javax.swing.JTable
import javax.swing.ListSelectionModel
import javax.swing.event.ChangeEvent
import javax.swing.table.TableCellEditor

internal open class GitRebaseCommitsTableView(
  val project: Project,
  val model: GitRebaseCommitsTableModel<*>,
  private val disposable: Disposable
) : JBTable(model) {

  companion object {
    val GRAPH_LINE_WIDTH: Float
      get() = JBUIScale.scale(1.5f)
    val GRAPH_NODE_WIDTH: Int
      get() = JBUI.scale(8)
    val DEFAULT_CELL_HEIGHT: Int
      get() = JBUI.scale(PaintParameters.ROW_HEIGHT)
    val GRAPH_COLOR = JBColor.namedColor("VersionControl.GitCommits.graphColor", JBColor(Color(174, 185, 192), Color(135, 146, 154)))
  }

  init {
    setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
    columnModel.selectionModel = object : DefaultListSelectionModel() {
      override fun setSelectionInterval(index0: Int, index1: Int) {
        val indexToForce = this@GitRebaseCommitsTableView.convertColumnIndexToView(GitRebaseCommitsTableModel.SUBJECT_COLUMN)
        super.setSelectionInterval(indexToForce, indexToForce)
      }
    }
    intercellSpacing = JBUI.emptySize()
    tableHeader = null
    installSpeedSearch()
    prepareCommitIconColumn()
    prepareSubjectColumn()
    installUndoRedoActions()
  }

  private fun installUndoRedoActions() {
    installAnActionWithShortcut(UndoAction(this), IdeActions.ACTION_UNDO)
    installAnActionWithShortcut(RedoAction(this), IdeActions.ACTION_REDO)
  }

  private fun installAnActionWithShortcut(action: AnAction, shortcutActionId: String) {
    action.registerCustomShortcutSet(KeymapUtil.getActiveKeymapShortcuts(shortcutActionId), this)
  }

  final override fun setSelectionMode(selectionMode: Int) {
    super.setSelectionMode(selectionMode)
  }

  private fun installSpeedSearch() {
    TableSpeedSearch(this) { o, cell -> o.toString().takeIf { cell.column == GitRebaseCommitsTableModel.SUBJECT_COLUMN } }
  }

  private fun prepareCommitIconColumn() {
    val commitIconColumn = columnModel.getColumn(GitRebaseCommitsTableModel.COMMIT_ICON_COLUMN)
    commitIconColumn.cellRenderer = GitRebaseCommitIconTableCellRenderer()
    adjustCommitIconColumnWidth()
  }

  private fun adjustCommitIconColumnWidth() {
    val contentWidth = 2 * GRAPH_NODE_WIDTH + UIUtil.DEFAULT_HGAP
    val column = columnModel.getColumn(GitRebaseCommitsTableModel.COMMIT_ICON_COLUMN)
    column.maxWidth = contentWidth
    column.preferredWidth = contentWidth
  }

  override fun prepareEditor(editor: TableCellEditor?, row: Int, column: Int): Component {
    onEditorCreate()
    return super.prepareEditor(editor, row, column)
  }

  protected open fun onEditorCreate() {}

  override fun removeEditor() {
    onEditorRemove()
    if (editingRow in 0 until rowCount) {
      setRowHeight(editingRow, DEFAULT_CELL_HEIGHT)
    }
    super.removeEditor()
  }

  override fun columnMarginChanged(e: ChangeEvent) {
    // same as JTable#columnMarginChanged except for it doesn't stop editing
    val tableHeader = getTableHeader()
    val resizingColumn = tableHeader?.resizingColumn
    if (resizingColumn != null && autoResizeMode == JTable.AUTO_RESIZE_OFF) {
      resizingColumn.preferredWidth = resizingColumn.width
    }
    resizeAndRepaint()
  }

  protected open fun onEditorRemove() {}

  private fun prepareSubjectColumn() {
    val subjectColumn = columnModel.getColumn(GitRebaseCommitsTableModel.SUBJECT_COLUMN)
    subjectColumn.cellRenderer = SubjectRenderer()
    subjectColumn.cellEditor = CommitMessageCellEditor(project, this, disposable)
  }

  internal fun getDrawNodeType(row: Int): NodeType = when {
    model.getElement(row).type == GitRebaseTodoModel.Type.NonUnite.KeepCommit.Edit -> NodeType.EDIT
    model.getElement(row).type !is GitRebaseTodoModel.Type.NonUnite.KeepCommit -> NodeType.NO_NODE
    model.getElement(row) is GitRebaseTodoModel.Element.UniteRoot -> NodeType.DOUBLE_NODE
    else -> NodeType.SIMPLE_NODE
  }

  private class UndoAction(private val table: GitRebaseCommitsTableView) : DumbAwareAction() {
    override fun actionPerformed(e: AnActionEvent) {
      table.model.undo()
    }
  }

  private class RedoAction(private val table: GitRebaseCommitsTableView) : DumbAwareAction() {
    override fun actionPerformed(e: AnActionEvent) {
      table.model.redo()
    }
  }
}

private class SubjectRenderer : ColoredTableCellRenderer() {
  companion object {
    private const val GRAPH_WIDTH = 20
    private const val CONNECTION_CENTER_X = GRAPH_WIDTH / 4
  }

  private var graphType: GraphType = GraphType.NoGraph
  private var rowHeight: Int = DEFAULT_CELL_HEIGHT

  private val connectionCenterY: Int
    get() = rowHeight / 2

  override fun paint(g: Graphics?) {
    super.paint(g)
    (g as Graphics2D).paintFixupGraph()
  }

  private fun Graphics2D.paintFixupGraph() {
    when (val type = graphType) {
      is GraphType.NoGraph -> {
      }
      is GraphType.FixupGraph -> {
        setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
        color = GRAPH_COLOR<commit_msg>[git] Interactive Rebase Dialog: Prohibit undo/redo actions during reword<commit_after>// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package git4idea.rebase.interactive.dialog

import com.intellij.icons.AllIcons
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.keymap.KeymapUtil
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.ui.CommitIconTableCellRenderer
import com.intellij.ui.ColoredTableCellRenderer
import com.intellij.ui.JBColor
import com.intellij.ui.SimpleTextAttributes
import com.intellij.ui.TableSpeedSearch
import com.intellij.ui.scale.JBUIScale
import com.intellij.ui.speedSearch.SpeedSearchUtil
import com.intellij.ui.table.JBTable
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.intellij.vcs.log.data.index.IndexedDetails
import com.intellij.vcs.log.paint.PaintParameters
import git4idea.rebase.interactive.GitRebaseTodoModel
import git4idea.rebase.interactive.dialog.GitRebaseCommitsTableView.Companion.DEFAULT_CELL_HEIGHT
import git4idea.rebase.interactive.dialog.GitRebaseCommitsTableView.Companion.GRAPH_COLOR
import git4idea.rebase.interactive.dialog.GitRebaseCommitsTableView.Companion.GRAPH_LINE_WIDTH
import git4idea.rebase.interactive.dialog.GitRebaseCommitsTableView.Companion.GRAPH_NODE_WIDTH
import git4idea.rebase.interactive.dialog.view.CommitMessageCellEditor
import java.awt.*
import javax.swing.DefaultListSelectionModel
import javax.swing.JTable
import javax.swing.ListSelectionModel
import javax.swing.event.ChangeEvent
import javax.swing.table.TableCellEditor

internal open class GitRebaseCommitsTableView(
  val project: Project,
  val model: GitRebaseCommitsTableModel<*>,
  private val disposable: Disposable
) : JBTable(model) {

  companion object {
    val GRAPH_LINE_WIDTH: Float
      get() = JBUIScale.scale(1.5f)
    val GRAPH_NODE_WIDTH: Int
      get() = JBUI.scale(8)
    val DEFAULT_CELL_HEIGHT: Int
      get() = JBUI.scale(PaintParameters.ROW_HEIGHT)
    val GRAPH_COLOR = JBColor.namedColor("VersionControl.GitCommits.graphColor", JBColor(Color(174, 185, 192), Color(135, 146, 154)))
  }

  init {
    setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
    columnModel.selectionModel = object : DefaultListSelectionModel() {
      override fun setSelectionInterval(index0: Int, index1: Int) {
        val indexToForce = this@GitRebaseCommitsTableView.convertColumnIndexToView(GitRebaseCommitsTableModel.SUBJECT_COLUMN)
        super.setSelectionInterval(indexToForce, indexToForce)
      }
    }
    intercellSpacing = JBUI.emptySize()
    tableHeader = null
    installSpeedSearch()
    prepareCommitIconColumn()
    prepareSubjectColumn()
    installUndoRedoActions()
  }

  private fun installUndoRedoActions() {
    installAnActionWithShortcut(UndoAction(this), IdeActions.ACTION_UNDO)
    installAnActionWithShortcut(RedoAction(this), IdeActions.ACTION_REDO)
  }

  private fun installAnActionWithShortcut(action: AnAction, shortcutActionId: String) {
    action.registerCustomShortcutSet(KeymapUtil.getActiveKeymapShortcuts(shortcutActionId), this)
  }

  final override fun setSelectionMode(selectionMode: Int) {
    super.setSelectionMode(selectionMode)
  }

  private fun installSpeedSearch() {
    TableSpeedSearch(this) { o, cell -> o.toString().takeIf { cell.column == GitRebaseCommitsTableModel.SUBJECT_COLUMN } }
  }

  private fun prepareCommitIconColumn() {
    val commitIconColumn = columnModel.getColumn(GitRebaseCommitsTableModel.COMMIT_ICON_COLUMN)
    commitIconColumn.cellRenderer = GitRebaseCommitIconTableCellRenderer()
    adjustCommitIconColumnWidth()
  }

  private fun adjustCommitIconColumnWidth() {
    val contentWidth = 2 * GRAPH_NODE_WIDTH + UIUtil.DEFAULT_HGAP
    val column = columnModel.getColumn(GitRebaseCommitsTableModel.COMMIT_ICON_COLUMN)
    column.maxWidth = contentWidth
    column.preferredWidth = contentWidth
  }

  override fun prepareEditor(editor: TableCellEditor?, row: Int, column: Int): Component {
    onEditorCreate()
    return super.prepareEditor(editor, row, column)
  }

  protected open fun onEditorCreate() {}

  override fun removeEditor() {
    onEditorRemove()
    if (editingRow in 0 until rowCount) {
      setRowHeight(editingRow, DEFAULT_CELL_HEIGHT)
    }
    super.removeEditor()
  }

  override fun columnMarginChanged(e: ChangeEvent) {
    // same as JTable#columnMarginChanged except for it doesn't stop editing
    val tableHeader = getTableHeader()
    val resizingColumn = tableHeader?.resizingColumn
    if (resizingColumn != null && autoResizeMode == JTable.AUTO_RESIZE_OFF) {
      resizingColumn.preferredWidth = resizingColumn.width
    }
    resizeAndRepaint()
  }

  protected open fun onEditorRemove() {}

  private fun prepareSubjectColumn() {
    val subjectColumn = columnModel.getColumn(GitRebaseCommitsTableModel.SUBJECT_COLUMN)
    subjectColumn.cellRenderer = SubjectRenderer()
    subjectColumn.cellEditor = CommitMessageCellEditor(project, this, disposable)
  }

  internal fun getDrawNodeType(row: Int): NodeType = when {
    model.getElement(row).type == GitRebaseTodoModel.Type.NonUnite.KeepCommit.Edit -> NodeType.EDIT
    model.getElement(row).type !is GitRebaseTodoModel.Type.NonUnite.KeepCommit -> NodeType.NO_NODE
    model.getElement(row) is GitRebaseTodoModel.Element.UniteRoot -> NodeType.DOUBLE_NODE
    else -> NodeType.SIMPLE_NODE
  }

  private abstract class DisabledDuringRewordAction(protected val table: GitRebaseCommitsTableView) : DumbAwareAction() {
    override fun update(e: AnActionEvent) {
      super.update(e)
      val presentation = e.presentation
      presentation.isEnabledAndVisible = true
      if (table.editingRow != -1) {
        presentation.isEnabledAndVisible = false
      }
    }
  }

  private class UndoAction(table: GitRebaseCommitsTableView) : DisabledDuringRewordAction(table) {
    override fun actionPerformed(e: AnActionEvent) {
      table.model.undo()
    }
  }

  private class RedoAction(table: GitRebaseCommitsTableView) : DisabledDuringRewordAction(table) {
    override fun actionPerformed(e: AnActionEvent) {
      table.model.redo()
    }
  }
}

private class SubjectRenderer : ColoredTableCellRenderer() {
  companion object {
    private const val GRAPH_WIDTH = 20
    private const val CONNECTION_CENTER_X = GRAPH_WIDTH / 4
  }

  private var graphType: GraphType = GraphType.NoGraph
  private var rowHeight: Int = DEFAULT_CELL_HEIGHT

  private val connectionCenterY: Int
    get() = rowHeight / 2

  override fun paint(g: Graphics?) {
    super.paint(g)
    (g as Graphics2D).paintFixupGraph()
  }

  private fun Graphics2D.paintFixupGraph() {
    when (val type = graphType) {
      is GraphType.NoGraph -> {
      }
      is GraphType.FixupGraph -> {
        setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
        color = GRAPH_COLOR
{'fraction_non_alphanumeric': 0.08232758620689655, 'fraction_numerical': 0.0069683908045977015, 'mean_word_length': 4.851618327028163, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2147891', 'n_tokens_mistral': 4467, 'n_tokens_neox': 4302, 'n_words': 794}