Inhalt eines JEditorPane kopieren

Ich habe 2 JEditorPane bei denen per Knopfdruck der Inhalt des ersten JEditorPane in den 2 JEditorPane kopiert werden soll.

Das kopieren des Textes funktioniert, allerdings werden Sonderzeichen nicht mitkopiert.

Hier ein Beispiel

Hier noch der Code:

package testJEditorPane;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.text.html.HTMLEditorKit;

public class TestJEditorPane2 extends JFrame{

	private JEditorPane tacomment1;
	private JEditorPane tacomment2;
	
	private JScrollPane spcomment1;
	private JScrollPane spcomment2;
	
	private JPanel pcomment1;
	private JPanel pcomment2;
	
	private JButton btnadd;
	
	public TestJEditorPane2(){	
		
		Container content = getContentPane();
		content.setLayout(new BorderLayout());
		
		tacomment1 = new JEditorPane();
		tacomment2 = new JEditorPane();
		
		tacomment1.setContentType("text/plain");
		tacomment1.setEditorKit(new HTMLEditorKit());
		tacomment1.setEditable(true);
		
		tacomment2.setContentType("text/plain");	
		tacomment2.setEditorKit(new HTMLEditorKit());
		tacomment2.setEditable(true);
		
		tacomment1.setMinimumSize(new Dimension(600,400));
		tacomment2.setMinimumSize(new Dimension(600,400));
		
		tacomment1.setPreferredSize(new Dimension(600,400));
		tacomment2.setPreferredSize(new Dimension(600,400));

		spcomment1 = new JScrollPane();
		spcomment1.setMinimumSize(new Dimension(600,400));
		spcomment1.setPreferredSize(new Dimension(600,400));
		
		spcomment2 = new JScrollPane();
		spcomment2.setMinimumSize(new Dimension(600,400));
		spcomment2.setPreferredSize(new Dimension(600,400));
		
		spcomment1.getViewport().setBackground(Color.WHITE);
		spcomment2.getViewport().setBackground(Color.WHITE);
		
		spcomment1.getViewport().add(tacomment1);
		spcomment2.getViewport().add(tacomment2);
		
		pcomment1 = new JPanel();
		pcomment2 = new JPanel();
		
		pcomment1.add(spcomment1);
		pcomment2.add(spcomment2);
		
		

		btnadd = new JButton("Content übertragen");
		btnadd.addActionListener(new ActionListener(){
				public void actionPerformed(ActionEvent ae){
					
					String vorgabe = getContent1();
					setContent2(vorgabe);
					
						
				}
		});
		
		btnadd.setPreferredSize(new Dimension(200,400));
		
		
		
		content.add(pcomment1, BorderLayout.WEST);
		content.add(pcomment2, BorderLayout.EAST);
		
		content.add(btnadd, BorderLayout.CENTER);
		

		
		pack();
		setLocationRelativeTo(null);
		
		addWindowListener(new WindowAdapter(){
		      public void windowClosing(WindowEvent e){
		    	  setVisible(false);
		    	  dispose();
		    	  
		      }
		});
	}
	
	public static void main(String[] args){
		
			JFrame frame = new TestJEditorPane2();
			frame.setVisible(true);
			
	}
	
	public String getContent1() {
		
		return tacomment1.getText();
		
	}
			
	public void setContent2(String text){
		
		tacomment2.setText(text);
	}
}

Tach,

https://docs.oracle.com/javase/7/docs/api/javax/swing/JEditorPane.html#getText()

Returns the text contained in this TextComponent in terms of the content type of this editor.

Wahrscheinlich läuft bei dieser Methode etwas schief… Muss es unbedingt ein JEditorPane sein, ginge nicht vielleicht auch eine JTextArea, wenn es eh um Plain-Text geht?

Eine JTextArea sollte es nicht sein, denn dort verhält sich der Tabulator anders.

Man sollte sich dazu vllt. mal die von JEditorPane überschriebenen get- und setText-Methoden im Quelltext ansehen. Wenn mich nicht alles täuscht, dan wird bei setText ein EditorKit verwendet, bei getText jedoch nicht. Ich würde nun rein Intuitiv, dieses EditorKit kopieren, bevor ich den Text kopiere.

Bei nochmaligem Überlegen, würde ich die JEditorPane erweitern, in welcher ich die getText-Methode überschreibe, die dann analog zu setText einen Writer über kit.write() beschreibt.

Außerdem fuchtele ich hier noch mit einem JDK 8 rum und von daher weiß ich nicht, ob dieser „Fauxpas“ (so will ich das mal nennen) inzwischen behoben wurde.

Alternative:
HTMLEditorKit komplett weglassen, wenn möglich:

		JFrame f = new JFrame();
		f.setLayout(new GridLayout(1, 3, 10, 0));
		JEditorPane p1 = new JEditorPane();
		JEditorPane p2 = new JEditorPane();
		// HTMLEditorKit e1 = new HTMLEditorKit();
		// HTMLEditorKit e2 = new HTMLEditorKit();
		p1.setContentType("text/plain");
		p2.setContentType("text/plain");
		// p1.setEditorKit(e1);
		// p2.setEditorKit(e2);
		JPanel p3 = new JPanel();
		JButton b = new JButton("replace");
		p3.add(b);
		f.add(p1);
		f.add(p2);
		f.add(p3);
		b.addActionListener((e) -> {
			System.out.println(p1.getText());
			p2.setText(p1.getText());
		});
		f.setSize(800, 400);

denn p1.getText() gibt andernfalls HTML-Text zurück.

Nein, ist auch mit neueren Versionen so.

Das macht überhaupt nichts. Wichtig ist vermutlich nur der ContentType und der wird samt Charset und weiteren möglichen Parametern im EditorKit gespeichert. Wenn ContentType und/oder EditorKit gar nicht beachtet werden, dann werden automatisch Defaults gesetzt und wenn diese nicht mit der Quelle identisch sind, wird auch nicht 1:1 kopiert - ist doch logisch, oder?

Und noch was: Wenn ich weiß, dass ich mit HTML arbeite (was ja bei JEditorPane der Fall ist), dann würde ich bei Sonderzeichen ohnehin mit HTML-En- bzw. -Decode arbeiten, aber das ist wohl nicht mehr nötig, sobald ContentType und Charset passt.

Also das ist richtig, getText() liefert HTML und das wird bei setText() auch interpretiert, allerdings wird zum Beispiel ein Tabulator nicht codiert, aber in p1 als Tabulator dargestellt.

Allerdings gibt es einen „Trick“: Wenn man in p2 etwas schreibt, dann ist setText() korrekt. Ich vermute, sobald man etwas eintippt, wird der ContentType, das Charset usw. gesetzt.

Wahrscheinlich ein Bug.

Wann immer getEditorKit() oder getEditorKitForContentType() aufgerufen wird, wird eines erstellt, wenn keines vorhanden ist. Das ist z.B. auch bei setText() der Fall, also ist es egal, ob vorher etwas eingetippt wurde, oder nicht. setText() wird ja auch bei Eingabe über den EDT aufgerufen, soweit ich weiß.

Und ja, natürlich ist es ein Bug, wenn eine get-Methode anders arbeitet, als eine set-Methode.

Dennoch kurz der Hinweis, dass
p1.setContentType("text/plain");
und
p1.setEditorKit(new HTMLEditorKit());
die erste Anweisung unsinnig ist (denn nach dem Setzen von HTMLEditorKit sollte der ContentType eigentlich „text/html“ sein…).


Das würde als „Workaround“ funktionieren:

		JFrame f = new JFrame();
		f.setLayout(new GridLayout(1, 3, 10, 0));
		JEditorPane p1 = new JEditorPane();
		JEditorPane p2 = new JEditorPane();
		HTMLEditorKit e1 = new HTMLEditorKit();
		HTMLEditorKit e2 = new HTMLEditorKit();
		p1.setEditorKitForContentType("text/html", e1); // also "text/plain"
		p2.setEditorKitForContentType("text/html", e2);
		JPanel p3 = new JPanel();
		JButton b = new JButton("replace");
		p3.add(b);
		f.add(p1);
		f.add(p2);
		f.add(p3);
		b.addActionListener((e) -> {
			System.out.println(p1.getText());
			p2.setText(p1.getText());
		});
		f.setSize(800, 400);
		f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		f.setVisible(true);

Ich glaub’ du suchst sowas

    btnadd.addActionListener(new ActionListener()
    {
        public void actionPerformed(ActionEvent ae)
        {
            try
            {
                Document doc1 = tacomment1.getDocument();
                Document doc2 = tacomment2.getDocument();
                String text = doc1.getText(0, doc1.getLength());
                doc2.insertString(0, text, null);
            }
            catch (BadLocationException e)
            {
                e.printStackTrace();
            }
        }
    });

Die Details des Zusammenspiels zwischen „Document“ und „Component“ sind ziemlich tricky (was nicht verwunderlich ist, wenn man bedenkt, dass man da „einfach mal so“ auch HTML darstellen kann), und ich fräse mich da immer nur so weit rein, wie ich es gerade brauche (und bisher habe ich noch nicht viel davon gebraucht…).

Zumindest nimmt er, wenn man über’s Document geht, die Tabs mit, und der Beschreibung nach war das ja vermutlich das, was du suchtest…

doc1.getText(...); liefert keinen HTML-Text zurück, deshalb funktioniert das…
Also mir fallen als Workaround nur diese zwei Möglichkeiten ein:

  1. doc1.getText(...); oder
  2. p1.setEditorKitForContentType("text/html", new HTMLEditorKit());,

wobei 1. womöglich besser ist.

Edit: Denn HTML(-Text) und Tabulatorzeichen können zusammen nicht richtig transportiert oder dargestellt werden. HTML sieht glaube ich keine Tabulatorzeichen vor…

Ja, in diesem Sinne ist der Text vom Document das „Model“, und der Text der JEditorPane selbst ist die „View“.

Da passiert halt viel magisches: Wenn man so einer JEditorPane etwas gibt, was in <html> tags steht, wird das interpretiert, durch das EditorKit und andere Sachen durchgejagt, und am Ende kommt formatierter Text auf dem Bildschirm raus. Wenn man dem Document aber „plain Text“ gibt, dann wird dieser Plain Text praktisch mit HTML formatiert, und beim JEditorPane hat man dann den HTML-Code, den man brauch, um den plain Text formatiert darzustellen.

Man könnte auch doc2.insertString(0, tacomment1.getText(), null); machen, um sich ~„den HTML-Quellcode“ anzuschauen.

(Ein Tab geht theoretisch mit &tab; - warum das nicht so codiert wird, ist eine der Detailfragen, mit denen man sich lange beschäftigen kann).

Noch schnell etwas am Rande…

text/html

HTML text. The kit used in this case is the class javax.swing.text.html.HTMLEditorKit which provides HTML 3.2 support.

alleine das würde mich schon vor dem Gebrauch von JEditorPane zurückschrecken lassen. Es wird einfach so oder so irgendwann „buggy“.

Gibt es &tab; überhaupt (anscheinend nicht, sonst wäre es auch übersetzt worden)? Ich verwende da immer „& # 0 9 ;“. Und genauso würde es auch codiert, wenn die setText()- mit der getText()-Methode übereinstimmen würde. Der Workaround mit Document funktioniert eigentlich bei jeder JTextComponent nur leider bei der JEditorPane nicht immer, wenn die EditorKits nicht „equal“ sind.

Als „Workaround“ würde ich das nicht direkt bezeichnen. Wenn man sowas hat wie
<html>Hello</html>
und fragt, „Was ist der ‚text‘“, dann wäre Hello eine legitme Antwort. Der Rest ist Formatierung.

Dass das „Document“ eine „low-level-Variante“ des Textes speichert, und „vor“ den Dingen wie EditorKit und Attributes liegt, ändert daran ja nichts. Aber wie gesagt: Es ist kompliziert. Und außer für sowas wie die Suchfunktion in Common und CommonUI - nur ein paar Utility-Klassen habe ich mich damit noch nicht soooo im Detail beschäftigt.

Das ist vermutlich das Entscheidende und die Krux am JEditorPane. Das EditorKit wird, soweit ich das überblicke, nur von dieser Klasse verwendet und deswegen überschreibt JEditorPane auch set- und getText() von JTextComponent. Bei getText() wird das Kit nicht verwendet, also bekommt man das, was im document steht. Und im document steht formatierter HTML-Text, weil es per überschiebener setText()-Methode dort formatiert eingepflegt wird. Greift man direkt auf document zu und kopiert auf diese Art, dann umgeht man das EditorKit und deswegen ist es für mich ein Workaround. An die legitime Antwort („Hello“) kommt man damit deswegen afaik auch nicht mehr, es sei denn, man hat das document mit diesem WA aus einer JTextComponent befüllt.
Das Ganze ist mMn nicht nur ein Bug, sondern ein unheimlich ekliger Pitfall. Vermutlich ist CBs Idee mit der JTextArea gar nicht mal eine soooo Üble.