Swing Einrücken mehrerer Zeilen per Tab in einem JTextPane/JTextArea

Albertus

Mitglied
Guten Abend

Also, ich stehe vor einem kleinen Problem. Da wir einen Editor für eine bestimmte Programmiersprache nicht plattformunabhängig verwenden können, sind wir daran, eine eigene Lösung zu schreiben )Mein Hintergrundwissen liegt eher in C(++) und Qt Applikationen, aber das Ganze wird jetzt halt in Java realisiert).

Nun zu meinem eigentlichen Problem. Ich verwende ein JTextPane, um darin Code schreiben zu können. Einzelne Zeilen mittels Tab einzurücken ist kein Problem, wenn ich aber mehrere Zeilen selektiere und Tab drücke, ersetzt es die Zeilen durch ein Tab, sprich wie folgt:

Ausgangstext
Code:
Zeile 1
Zeile 2
Zeile 3
Zeile 4

Wenn alle 4 Zeilen markiert und Tab gedrückt wird, sollte es nachher so aussehen:

Code:
<Tab>Zeile 1
<Tab>Zeile 2
<Tab>Zeile 3
<Tab>Zeile 4

Das tut es aber nicht, denn es sieht nur so aus:

Code:
<Tab>

Sprich der Text wird mit einem Tab ersetzt und nicht über mehrere Zeilen gleich eingerückt. Gibt es da eine einfache und vorallem saubere Lösung, ich habe mir mal diesen Code angeschaut, aber dort kann ich über den linken Editorrand haus zurücltaben, sprich nicht sehr sauber:

Indentation not possible in JTextPane?

Würde mich über eine Antwort freuen,

Gruss
 
Was mir einfällt wäre einfach den selectedText zwischen speichern, jede Zeile splitten und dann mit vorgesetztem Tab wieder einfügen.. wahrscheinlich nicht ultra performant, aber sollte seinen Zweck erfüllen
 
Wie in dem Beispiel schon angedeutet, brauchst du eine eigene [JAPI]TextAction[/JAPI]:
Java:
public class IndentAction extends TextAction {
	public static final String NAME = "indent";
	
	public IndentAction() {
		super(NAME);
	}
	
	@Override
	public void actionPerformed(final ActionEvent e) {
		final JTextComponent tc = getTextComponent(e);
		final Action insertTabAction = tc.getActionMap().get(DefaultEditorKit.insertTabAction);
		final int start = tc.getSelectionStart();
		final int end = tc.getSelectionEnd();
		if (start == end) { // keine Selektion
			insertTabAction.actionPerformed(e);
		} else {
			final Document doc = tc.getDocument();
			try {
				final int lastPos = Utilities.getRowStart(tc, start);
				int pos = Utilities.getRowStart(tc, end);
				if (lastPos == pos) { // Selektion nur innerhalb einer Zeile
					insertTabAction.actionPerformed(e);
				} else {
					while (true) { // von unten nach oben einen Tab an den Anfang der zeile einfügen
						doc.insertString(pos, "\t", null);
						if (pos <= lastPos) {
							break;
						}
						pos = Utilities.getRowStart(tc, pos - 1);
					}
				}
			} catch (final BadLocationException e1) {
				e1.printStackTrace();
			}
		}
	}
}
Diese kannst du so an deine Textkomponente binden:
Java:
final JTextPane tp = new JTextPane();
tp.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0) /* Tab */, IndentAction.NAME);
tp.getActionMap().put(IndentAction.NAME, new IndentAction());

Für die Ausrückung dann analog:
Java:
public class OutdentAction extends TextAction {
	public static final String NAME = "outdent";
	
	public OutdentAction() {
		super(NAME);
	}
	
	@Override
	public void actionPerformed(final ActionEvent e) {
		// TODO
	}
}
Java:
final JTextPane tp = new JTextPane();
tp.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK) /* Shift+Tab */, OutdentAction.NAME);
tp.getActionMap().put(OutdentAction.NAME, new OutdentAction());

Ich glaube, wenn man es ganz sauber machen will, steuert man das über ein eigenes [JAPI]DefaultEditorKit[/JAPI]. Da bin ich mir gerade aber unsicher.
 
Hi

Okay, vielen Dank. das funktioniert bereits super. Jetzt muss ich nur noch was für die Outdent Funktion schreiben. Das Problem liegt hier darin, dass der Block (vor dem eigentlichen Code) aus Tabs (oder im dümmsten Fall auch Leerzeichen) bestehen kann, sprich ich kann schlecht einfach den ersten Tab rausnehmen (Per Escape Sequenz), bin also noch ein bisschen am tüfteln.

Gruss
 
Zuletzt bearbeitet:

Zurück
Oben