Erste Schritte Texteditor mit Formatierungen

Blackhole16

Bekanntes Mitglied
Moin com,

Ich bin gerade dabei einen Texteditor in swing zu proggen, der genau meinen Ansprüchen entspricht ;)

Ich habe dazu einen Einfachen aus dem Internet "geklaut" und hab ihn zuerst extrem meinem Stil angepasst und auch schon einige weitere Funktionen eingebaut.
Dabei ist mir schon einmal eine Frage gekommen: Welche TextPane sollte ich verwenden? JEditorPane, JTextArea, JTextPane etc. ? Ich würde gerne das unterbrechen der Wöter am Ende der Zeile verhindern. Bei der JTextArea geht das leicht mit setWrapStyleWord. Geht das auch bei z.B. der JEditorPane?
Das eigentliche riesige Problem ist aber:
Ich möchte jetzt Formatierungen einbauen, die einzelne Wörter betreffen. Wie kann ich dies bereitstellen? Ich finde dazu keinen Ansatz. Wenn ich es mit HTML mache, kann ich zwischendrin keine Formatierungen mehr vornehmen, da ich das ja an eine Bestimmte Stelle einfügen muss, die ich nicht kenne. Denn getCaretPosition missachtet ja die HTML tags...

Ich habe schon lange überlegt, aber irgentwie kam ich auf keine Idee. Könnt ihr mir auf die Sprünge helfen?

Danke im Voraus für eure Antworten

mfg
BH16
 

Volvagia

Top Contributor
Ich hab mir mal sowas per RegEx geschrieben. Wie es genau funktioniert weiß ich nicht mehr, ist schon recht lange her. Bin der Call Hirarchie gefolgt, wie es aussieht wird einfach der Text per parseString gesetzt und die Parser in der Liste oben schauen es durch und setzen das AttributSet. Vielleicht kannst du damit ja was anfangen.

Ich bin schon zu müde zum testen, aber wird bei JTextPane und JEditorPane nicht der Line Wrap je nach JScrollPane geregelt? Also wenn eine Horizontale da ist geht es weiter und wenn nicht wird umgebrochen?

Java:
package at.atc.ows.client.application.wiki.reader;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.Position;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyledEditorKit;

public class WikiTextPane extends JEditorPane
{
	public static final Object LINK = new StringBuffer("link");
	
	private static final WikiTextParsers[] PARSERS = {
		new BoldParser(),	
		new LinkParser(),
		new ItalicParser(),
		new UnderlineParser(),
		new NamedLinkParser()
	};
	
	private StyledDocument document;
	private WikiTextPaneModel model;
	
	public WikiTextPane(WikiTextPaneModel model)
	{
		setEditorKit(new StyledEditorKit());
		
		MouseAdapter mouseController = new MouseController();
		addMouseListener(mouseController);
		addMouseMotionListener(mouseController);
		
		document = (StyledDocument) getDocument();
		this.model = model;
	}
	public void parseString(String text)
	{
		int offset = 0;
		try {
			document.remove(0, document.getLength());

			while(text != null && text.length() != 0) {
				int firstCodeParserLocation = Integer.MAX_VALUE;
				WikiTextParsers firstCodeParser = null;
				
				for(WikiTextParsers parsers:PARSERS) {
					int parserLocation = parsers.getFirstMatchLocation(text);
					
					if(parserLocation != -1 && parserLocation < firstCodeParserLocation) {
						firstCodeParser = parsers;
						firstCodeParserLocation = parserLocation;
					}
				}
				
				if(firstCodeParser == null) {
					document.insertString(offset, text, null);
					
					text = null;
					continue;
				}
				
				String beforTextPiece = text.substring(0, firstCodeParserLocation);
				
				document.insertString(offset, beforTextPiece, null);
				offset+= beforTextPiece.length();
				text = text.substring(firstCodeParserLocation);

				String[] result = firstCodeParser.addTextToDocument(offset, text, document, model);
				
				text = result[0];
				offset = Integer.parseInt(result[1]);
			}
		}
		catch (BadLocationException e)
		{
			e.printStackTrace();
		}
	}
	private class MouseController extends MouseAdapter
	{
		private boolean handCursor;

		public void mouseMoved(MouseEvent e)
		{
			Element c = characterElementAt(e);
			if (c != null) {
				if(c.getAttributes().getAttribute(LINK) != null) {
					if(!handCursor) {
						setCursor(new Cursor(Cursor.HAND_CURSOR));
						handCursor = true;	
					}
				}
				else if(handCursor) {
					setCursor(new Cursor(Cursor.TEXT_CURSOR));
					handCursor = false;
				}
			}
		}
		public void mousePressed(MouseEvent e)
		{
			if (!SwingUtilities.isLeftMouseButton(e))
				return;

			Element c = characterElementAt(e);
			if(c == null) {
				return;
			}
			
			Object linkAttribute = c.getAttributes().getAttribute(LINK);
			if(linkAttribute != null) {
				model.openArticle(linkAttribute.toString());
			}
		}
		private Element characterElementAt(MouseEvent e)
		{
			JEditorPane p = (JEditorPane) e.getComponent();

			Position.Bias[] bias = new Position.Bias[1];
			int position = p.getUI().viewToModel(p, e.getPoint(), bias);

			if (bias[0] == Position.Bias.Backward && position != 0) {
				position--;
			}

			Element c = ((StyledDocument) p.getDocument()).getCharacterElement(position);

			return c;
		}
	}
	private static class LinkParser extends WikiTextParsers
	{
		public String getRegEx()
		{
			return("(.*?)\\[article\\](.+?)\\[/article\\](.*?)");
		}
		public int getFirstMatchLocation(String text)
		{
			Matcher matcher = pattern.matcher(text);
			
			if(!matcher.matches() || matcher.groupCount() == 0) {
				return(-1);
			}
			return(matcher.group(1).length());
		}
		public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
		{
			Matcher matcher = pattern.matcher(text);

			if(!matcher.matches() || matcher.groupCount() < 2) {
				return(new String[] {
						text, String.valueOf(offset)
				});
			}
			
			String matchText = matcher.group(2);
			String postText = matcher.group(3);
			
			MutableAttributeSet attributeSet = new SimpleAttributeSet();
			attributeSet.addAttribute(LINK, matchText);
			attributeSet.addAttribute(StyleConstants.Foreground, model.existArticle(matchText) ? Color.BLUE : Color.RED);
									
			document.insertString(offset, matchText, attributeSet);
			offset+= matchText.length();
								
			return(new String[] {
					postText, String.valueOf(offset)
			});
		}
	}
	private static class NamedLinkParser extends WikiTextParsers
	{
		public String getRegEx()
		{
			return("(.*?)\\[article\\=(.+?)\\](.+?)\\[/article\\](.*?)");
		}
		public int getFirstMatchLocation(String text)
		{
			Matcher matcher = pattern.matcher(text);
			
			if(!matcher.matches() || matcher.groupCount() == 0) {
				return(-1);
			}
			return(matcher.group(1).length());
		}
		public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
		{
			Matcher matcher = pattern.matcher(text);
			if(!matcher.matches()) {
				return(new String[] {
						text, String.valueOf(offset)
				});
			}
			
			String articleName = matcher.group(2);
			String linkText = matcher.group(3);
			String postText = matcher.group(4);
				
			MutableAttributeSet attributeSet = new SimpleAttributeSet();
			attributeSet.addAttribute(LINK, articleName);
			attributeSet.addAttribute(StyleConstants.Foreground, model.existArticle(articleName) ? Color.BLUE : Color.RED);
			
			document.insertString(offset, linkText, attributeSet);
			offset+= linkText.length();
								
			return(new String[] {
					postText, String.valueOf(offset)
			});
		}
	}
	private static class ItalicParser extends WikiTextParsers
	{
		public String getRegEx()
		{
			return("(.*?)\\[i\\](.+?)\\[/i\\](.*?)");
		}
		public int getFirstMatchLocation(String text)
		{
			Matcher matcher = pattern.matcher(text);
			
			if(!matcher.matches() || matcher.groupCount() == 0) {
				return(-1);
			}
			return(matcher.group(1).length());
		}
		public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
		{
			Matcher matcher = pattern.matcher(text);

			if(!matcher.matches() || matcher.groupCount() < 2) {
				return(new String[] {
						text, String.valueOf(offset)
				});
			}
			
			String matchText = matcher.group(2);
			String postText = matcher.group(3);
				
			MutableAttributeSet attributeSet = new SimpleAttributeSet();
			attributeSet.addAttribute(StyleConstants.Italic, true);
									
			document.insertString(offset, matchText, attributeSet);
			offset+= matchText.length();
								
			return(new String[] {
					postText, String.valueOf(offset)
			});
		}
	}
	private static class UnderlineParser extends WikiTextParsers
	{
		public String getRegEx()
		{
			return("(.*?)\\[u\\](.+?)\\[/u\\](.*?)");
		}
		public int getFirstMatchLocation(String text)
		{
			Matcher matcher = pattern.matcher(text);
			
			if(!matcher.matches() || matcher.groupCount() == 0) {
				return(-1);
			}
			return(matcher.group(1).length());
		}
		public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
		{
			Matcher matcher = pattern.matcher(text);

			if(!matcher.matches() || matcher.groupCount() < 2) {
				return(new String[] {
						text, String.valueOf(offset)
				});
			}
			
			String matchText = matcher.group(2);
			String postText = matcher.group(3);
				
			MutableAttributeSet attributeSet = new SimpleAttributeSet();
			attributeSet.addAttribute(StyleConstants.Underline, true);
									
			document.insertString(offset, matchText, attributeSet);
			offset+= matchText.length();
								
			return(new String[] {
					postText, String.valueOf(offset)
			});
		}
	}
	private static class BoldParser extends WikiTextParsers
	{
		public String getRegEx()
		{
			return("(.*?)\\[b\\](.+?)\\[/b\\](.*?)");
		}
		public int getFirstMatchLocation(String text)
		{
			Matcher matcher = pattern.matcher(text);
			
			if(!matcher.matches() || matcher.groupCount() == 0) {
				return(-1);
			}
			return(matcher.group(1).length());
		}
		public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
		{
			Matcher matcher = pattern.matcher(text);

			if(!matcher.matches() || matcher.groupCount() < 2) {
				return(new String[] {
						text, String.valueOf(offset)
				});
			}
			
			String matchText = matcher.group(2);
			String postText = matcher.group(3);
				

			MutableAttributeSet attributeSet = new SimpleAttributeSet();
			attributeSet.addAttribute(StyleConstants.Bold, true);
									
			document.insertString(offset, matchText, attributeSet);
			offset+= matchText.length();
								
			return(new String[] {
					postText, String.valueOf(offset)
			});
		}
	}
		
	private static abstract class WikiTextParsers
	{
		protected Pattern pattern;
		
		public WikiTextParsers()
		{
			super();
			pattern = Pattern.compile(getRegEx(), Pattern.DOTALL);
		}
		public abstract String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException;
		public abstract int getFirstMatchLocation(String text);
		public abstract String getRegEx();
	}
	public static abstract class WikiTextPaneModel
	{
		public abstract void openArticle(String articleTitle);
		public abstract boolean existArticle(String articleTitle);
	}
}
 

Blackhole16

Bekanntes Mitglied
Ich hab mir mal sowas per RegEx geschrieben. Wie es genau funktioniert weiß ich nicht mehr, ist schon recht lange her. Bin der Call Hirarchie gefolgt, wie es aussieht wird einfach der Text per parseString gesetzt und die Parser in der Liste oben schauen es durch und setzen das AttributSet. Vielleicht kannst du damit ja was anfangen.

Also erstmal: :toll::applaus::toll::applaus::toll::applaus:

Und jetzt: Ich habe kein Wort verstanden ;'( Ich bin Anfänger (oder warum würde icvh sonst im Anfängerforum schreiben) :) Kannst du mir das pls auch so erklären, dass ich als DAU verstehe =? :D

Ich bin schon zu müde zum testen, aber wird bei JTextPane und JEditorPane nicht der Line Wrap je nach JScrollPane geregelt? Also wenn eine Horizontale da ist geht es weiter und wenn nicht wird umgebrochen?

ja das weiß ich, aber mir geht es darum, dass ich nicht mitten im Wort umbrechen möchte. Und das ist bei der Variante leider so.

Danke auf jeden Fall scchon mal.

mfg
BH16

PS: Wie binde ich deine WikiTextPane ein? was muss in den Konstruktor =? ???:L
 

diggaa1984

Top Contributor
Um welche Art Formatierung geht es denn, soll es eine Art Syntaxhighlighting werden? Ich denke da sollten sich genügend Beispiele finden lassen, ansonsten müsstest du ein wenig konkreter werden.
 

Blackhole16

Bekanntes Mitglied
Ich meine Damit, dass ich wie in Word einfach mal ein Wort mitten im Text fett oder rot machen kann :p

Das soll aber halt trotzdem mit abgespeichert werden und man soll dann ganz einfach mittendrin weiter schreiben können. Ich brauche in dieser Hinsicht einen etwas größeren Denkanstoß. Und Beispiele habe ich leider nicht gefunden. Vielleicht habe ich auch einfach falsche Suchbegriffe genutzt...

mfg
BH16
 

ARadauer

Top Contributor
dass ich wie in Word einfach mal

Ich habe kein Wort verstanden ;'( Ich bin Anfänger

nur weil man in word so einfach mal ein icon anklicken kann, heißt es nicht, dass die dahinter liegende implementierung auch so einfach ist...

Alos ich denke, dass du Grundsätzlich mal zwischen dem Text der dir angezeigt wird und dem Text den dein Datenmodel repräsentiert unterscheiden musss. Ich bin mir nicht sicher ob dir das klar ist...
 

Blackhole16

Bekanntes Mitglied
Das ist mir klar, deswegen finde ich das ja auch so schwer einen Ansatz zu finden, wie das gehen soll...

Deswegen bin ich ja hier im Forum...

mfg
BH16
 

Volvagia

Top Contributor
Mach dir erst mal Gedanken darüber, wie man Text farbig oder B/I/U bekommt. Google anwerfen, Beispiele raussuchen und damit herumspielen was das Zeug hält.

JTextPane.setCharacterAttributes(AttributeSet attr, boolean replace) : JTextPanejavax.swingJava by API

Über das Abspeichern kannst du dir später immer noch Gedanken machen, eins nach dem anderen. Zwar grundsätzlich keine so tolle Idee, aber wenn du nichtmal weißt wie du es machen könntest kannst du das ja mal vernachlässigen.

Du könntest beispielsweiße vor oder nach den Nutzerdaten die Daten der Attribute mit einer Liste, welche Textpassagen dieses oder ein equales Attribut benutzt. Oder auch vollkommen anderst.


Bei dieser weißen Seite findet man sehr viele Codebeispiele zu den unterschiedlichsten Zeug.
 

Blackhole16

Bekanntes Mitglied
Danke, dein Link hat mich über google auf diese Seite gebracht, mit der ich das ganze jetzt gemacht habe. Ich denke, dass ich das Abspeichern so machen werde, dass ich ich vor dem Text ein paar Zeilen Formatierungen machen werde und die Zeichenzahlen mit deren Einstellungen speichern werde, die verändert wurden.

Echt thx, wenn auch nur über 5 Ecken :p

mfg
BH16
 

Ähnliche Java Themen

Neue Themen


Oben