package de.duehl.swing.ui.area;

/*
 * Copyright 2018 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;

/**
 * Diese Klasse stellt Methoden bereit, mit denen sich an bestimmte Tasten (Ctrl-Enter und
 * Ctrl-Tabulator) die Aktionen binden lassen, entsprechenden Text (ein Tabulatorzeichen bzw. einen
 * Zeilenumbruch in die Textarea einzufügen. einzufügen. Benötigt wird dies dann, wenn das
 * FocusTraversal-Verhalten der TextArea umgestellt wird. Normalerweise behandeln TextAreas Tab und
 * Enter als Texteingaben, was stört, wenn man hindurch "tabben" möchte.
 *
 * Wird von invertFocusTraversalBehaviour() in basics.gui.ui.area..AreaFocusTraversal benötigt, um
 * die InputMap einer Textarea zu erweitern, um einen Tabulator mit Ctrl-Tab einzufügen.
 *
 * Vergleiche http://www.javalobby.org/java/forums/t20457.html
 *
 * @version 1.01     2018-04-05
 * @author Christian Dühl
 */

public class TextInserter extends AbstractAction {

    private static final long serialVersionUID = 1L;

    /** Die Textarea, um die es geht. */
    private JTextArea area;

    /** Der einzufügende String. */
    private String insertable;

    /**
     * Konstruktor. Der Konstruktor ist privat, da es nichts nutzt, solche Objekte zu erstellen,
     * ohne sie mit applyTabBinding (oder einer entsprechenden Methode) auch in passend die
     * InputMap einzufügen.
     *
     * @param area
     *            Die Textarea, um die es geht.
     * @param insertable
     *            Der einzufügende String.
     */
    private TextInserter(JTextArea area, String insertable) {
        this.area = area;
        this.insertable = insertable;
    }

    /**
     * Erzeugt ein neues TextInserter-Objekt und fügt es passend in die richtige InputMap der Area
     * ein.
     *
     * @param area
     *            Die Textarea, um die es geht.
     */
    public static void applyTabBinding(JTextArea area) {
        applyKeyBindung(KeyEvent.VK_TAB, area);
    }

    /**
     * Erzeugt ein neues TextInserter-Objekt und fügt es passend in die richtige InputMap der Area
     * ein.
     *
     * @param area
     *            Die Textarea, um die es geht.
     */
    public static void applyEnterBinding(JTextArea area) {
        applyKeyBindung(KeyEvent.VK_ENTER, area);
    }

    /**
     * Erzeugt ein neues TextInserter-Objekt und fügt es passend in die richtige InputMap der Area
     * ein.
     *
     * @param area
     *            Die Textarea, um die es geht.
     */
    private static void applyKeyBindung(int keyEvent, JTextArea area) {
        if (keyEvent != KeyEvent.VK_TAB && keyEvent != KeyEvent.VK_ENTER) {
            throw new IllegalArgumentException("Illegales keyEvent " + keyEvent);
        }

        /* Wir beschaffen uns die InputMap, die verwendet wird, wenn die Textarea den Fokus hat: */
        InputMap focussedInputMap =
                area.getInputMap(JComponent.WHEN_FOCUSED);

        /* Wir beschaffen uns den KeyStroke für Ctrl-Tab: */
        //int ctrlMask = KeyEvent.CTRL_MASK + KeyEvent.CTRL_DOWN_MASK;
        int ctrlMask = KeyEvent.CTRL_DOWN_MASK;
        KeyStroke ctrlKeyEvent = KeyStroke.getKeyStroke(keyEvent, ctrlMask);

        /* Nun fügen wir diesem KeyStroke eine passende Behandlung mit dem Namen "tab" hinzu: */
        String name;
        String insertedText;
        if (keyEvent == KeyEvent.VK_TAB) {
            name = "tab";
            insertedText = "\t";
        }
        else {
            name = "enter";
            insertedText = System.getProperty("line.separator");
        }
        focussedInputMap.put(ctrlKeyEvent, name);

        /* ... und definieren eben diese Behandlung "tab" in der ActionMap der TextArea: */
        ActionMap actionMap = area.getActionMap();
        TextInserter inserter = new TextInserter(area, insertedText);
        actionMap.put(name, inserter);
    }

    /**
     * Fügt beim Auftreten der Aktion "tab" nun auch wirklich ein Tab und bei "enter" einen
     * Zeilenumbruch ein.
     */
    @Override
    public void actionPerformed(ActionEvent event) {
        String selectedText = area.getSelectedText();
        if (null != selectedText && !selectedText.isEmpty()) {
            area.replaceSelection(insertable);
        }
        else {
            area.insert(insertable, area.getCaretPosition());
        }
    }

}

