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.AWTKeyStroke;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JTextArea;

/**
 * Diese Klasse bietet eine Methoden, um Strg-Tab aus dem Focus-Traversal von einer JTextArea zu
 * entfernen und bindet für Strg-Tab und Strg-Enter die Eingabe vom Tabulatorzeichen bzw. eines
 * Zeilenumbruchs an die Area. Mit dem normalen Enter wird ein Button ausgelöst.
 *
 * Siehe basics.gui.ui.area.TextInserter
 *
 * Vergleiche http://www.javalobby.org/java/forums/t20457.html
 *
 * @version 1.01     2018-04-05
 * @author Christian Dühl
 */

public class AreaFocusTraversal {

    /**
     * Mit dieser Methode wird Strg-Tab aus dem Traversal-Behaviour rausgenommen und ermöglicht,
     * dass mit Strg-Tab ein Tab eingefügt werden kann.
     *
     * Wenn man in der TextArea oft Tabulatoren eintippen muss, ist es unter Umständen sinnvoller,
     * diese Modifikation nicht vorzunehmen!
     *
     * Vergleiche http://www.javalobby.org/java/forums/t20457.html
     *
     * @param textArea
     *            Zu bearbeitende TextArea.
     * @param button
     *            Button, der ausgelöst wird, wenn der Benutzer in der Textarea Enter eingibt.
     */
    public void invertFocusTraversalBehaviour(JTextArea textArea, JButton button) {
        /*
         * Zunächst einmal beschaffen wir uns die Mengen mit den Tasten für
         * vorwärts und rückwärts (also normalerweise Tab und Shift-Tab, aber
         * auch Ctrl-Tab für Vorwärts):
         */
        Set<AWTKeyStroke> forwardKeys = textArea.getFocusTraversalKeys(
                KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
        Set<AWTKeyStroke> backwardKeys = textArea.getFocusTraversalKeys(
                KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);

        /*
         * Wenn beide Mengen nicht jeweils ein Element haben, verändern wir sie
         * nicht:
         */
        if (forwardKeys.size() == 1 && backwardKeys.size() == 1) {
            /* Wir beschaffen uns die beiden einzigen Elemente dieser Mengen: */
            final AWTKeyStroke forwardKeyStroke = forwardKeys.iterator().next();
            final AWTKeyStroke backwardKeyStroke = backwardKeys.iterator().next();

            /* ... und deren Modifier: */
            final int forwardModifier = forwardKeyStroke.getModifiers();
            final int backwardModifier = backwardKeyStroke.getModifiers();

            /* Wir definieren die Maske für Ctrl und ShiftCtrl: */
            final int noModifierMask = 0;
            //final int ctrlMask = KeyEvent.CTRL_MASK + KeyEvent.CTRL_DOWN_MASK;
            //final int shiftMask = KeyEvent.SHIFT_MASK + KeyEvent.SHIFT_DOWN_MASK;
            final int ctrlMask = KeyEvent.CTRL_DOWN_MASK;
            final int shiftMask = KeyEvent.SHIFT_DOWN_MASK;
            final int shiftCtrlMask = shiftMask + ctrlMask;

            /*
             * Wir überprüfen, dass beide KeyStrokes die Tabulatortaste beinhalten und dass die
             * Modifier die CtrlMask beinhalten:
             */
            int tabKey = KeyEvent.VK_TAB;
            int forwardKey = forwardKeyStroke.getKeyCode();
            int backwardKey = backwardKeyStroke.getKeyCode();
            if (forwardKey == tabKey && backwardKey == tabKey
                    && (forwardModifier & ctrlMask) > 0
                    && (backwardModifier & shiftCtrlMask) > 0) {
                /*
                 * Wir definieren zwei neue Mengen (mit der Kapazität 1) für vorwärts und
                 * rückwärts:
                 */
                Set<AWTKeyStroke> newForwardKeys = new HashSet<AWTKeyStroke>(1);
                Set<AWTKeyStroke> newBackwardKeys = new HashSet<AWTKeyStroke>(1);

                /*
                 * Wir fügen diesen Mengen nun für Vorwärts nur die Tabulator-Taste ohne Modifier
                 * und für Rückwärts nur die Tabulatortaste mit dem Shift-Modifier zu:
                 */
                AWTKeyStroke newForwardKeyStroke =
                        AWTKeyStroke.getAWTKeyStroke(tabKey, noModifierMask);
                newForwardKeys.add(newForwardKeyStroke);

                AWTKeyStroke newBackwardKeyStroke =
                        AWTKeyStroke.getAWTKeyStroke(tabKey, shiftMask);
                newBackwardKeys.add(newBackwardKeyStroke);

                /*
                 * Und nun tragen wir die neuen Mengen in der TextArea ein, wofür wir aus den
                 * Mengen allerdings noch eine nicht-modifizierbare Sicht auf diese Mengen erzeugen
                 * müssen:
                 */
                textArea.setFocusTraversalKeys(
                        KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
                        Collections.unmodifiableSet(newForwardKeys));
                textArea.setFocusTraversalKeys(
                        KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
                        Collections.unmodifiableSet(newBackwardKeys));

                /*
                 * Jetzt legen wir fest, dass man mit Ctrl-Tab ein normales Tabulatorzeichen an der
                 * Stelle des Textcursors in die TextArea einfügt:
                 */
                TextInserter.applyTabBinding(textArea);

                /*
                 * Da wir diese Modifikationen eh vornehmen, wird nun auch für Ctrl-Enter das
                 * Einfügen eines Zeilenumbruchs in den Text festgelegt:
                 */
                TextInserter.applyEnterBinding(textArea);

                /*
                 * Nun fügen wir auch noch das Verhalten für ein einfaches
                 * Return (Auslösen des StartButtons hinzu:
                 */
                textArea.addKeyListener(new KeyListener() {
                    @Override
                    public void keyTyped(KeyEvent event) {
                    }
                    @Override
                    public void keyReleased(KeyEvent event) {
                    }
                    @Override
                    public void keyPressed(KeyEvent event) {
                        if (event.getKeyCode() == KeyEvent.VK_ENTER
                                && event.getModifiersEx() == 0) {
                            event.consume();
                            button.doClick();
                        }
                    }
                });
            }
        }
    }

}
