# TextEditor - Zeilennummerierung



## mangaka (12. Okt 2005)

Guten Abend,
ich bin neu in der Programmierung unter Java, kenn mich aber mit der Materie selbst bestens aus, da ich vorher unter php und delphi programmiert habe.

Ich programmiere grad einen einfachen TextEditor und möchte gerne die Anzahl der Zeilen links neben der TextArea angeben und die akutelle Zeile z.b. gelb markieren.

meine idee wäre es die zahl 1 von anfang an im editor schon anzuzeigen, denn ab eins beginnen wir ja.
und jedesmal, wenn man enter drückt soll die nächste zahl eingefügt werden, jedoch weis ich nicht wie man auf enter mit hilfe des actionlisteners reagiert(vllt geht das ja auch anders).

und wie ich die zeile färben soll weis ich ehrlich gesagt überhaupt nicht^^


habt ihr ideen? vllt codeschnippsel?
THX im vorraus


ps:
 ich auch gern wissen was ihr vom code meines editors haltet:


```
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;

public class JTextEditor extends JFrame implements ActionListener, WindowListener {
JTextArea text;

  public JTextEditor() {
    super("JTextEditor - v.3/5");
    setLayout(new BorderLayout(0,0));
    
    
    text = new JTextArea(null,20,60);
    JScrollPane scrollpane = new JScrollPane(text);
    
    
    JMenuBar menuBar = new JMenuBar();

    JMenu menuFile = new JMenu("File");
    menuFile.setMnemonic(KeyEvent.VK_F);

    JMenuItem menuFileOpen = new JMenuItem("Open");
    menuFileOpen.setMnemonic(KeyEvent.VK_O);

    JMenuItem menuFileSave = new JMenuItem("Save");
    menuFileSave.setMnemonic(KeyEvent.VK_S);

    JMenuItem menuFileClose = new JMenuItem("Close");
    menuFileClose.setMnemonic(KeyEvent.VK_C);

    menuBar.add(menuFile);
    menuFile.add(menuFileOpen);
    menuFile.add(menuFileSave);
    menuFile.add(menuFileClose);
    menuBar.add(menuFile);
    setJMenuBar(menuBar);
    
    menuFileOpen.addActionListener(this);
    menuFileSave.addActionListener(this);
    menuFileClose.addActionListener(this);
    
    
    this.getContentPane().add(scrollpane,BorderLayout.CENTER);
    
    addWindowListener( this );
}



  public void actionPerformed(ActionEvent e) {
  	 JFileChooser fc = new JFileChooser();
     if (e.getActionCommand().equals("Open")) {
		int odlg = fc.showOpenDialog( null );
		 	
		if(odlg == fc.APPROVE_OPTION) {
		  try {
     		  
			File filename = fc.getSelectedFile();
            FileReader file = new FileReader(filename); 
        	//StringBuffer, da nur er Veränderungen im String zulässt
        	StringBuffer message = new StringBuffer();      
        	char c; 
        	while (file.ready()) 
        	{ 
          	  c = (char)file.read();
          	  message.append(c); 
        	} 
        	file.close(); 
        	//Gibt das Objekt message als String wieder
        	text.setText(message.toString()); 	     			
     	  }
     	  catch(IOException ex) {
		    JOptionPane.showMessageDialog(null,
    	    ex.getMessage(),
    	    "ERROR",
    	    JOptionPane.ERROR_MESSAGE);
          } 
        }
     }

     if (e.getActionCommand().equals("Save")) {
	   int sdlg = fc.showSaveDialog(null); 
	   
	   if(sdlg == fc.APPROVE_OPTION){
	     try {
	       FileWriter file = new FileWriter(fc.getSelectedFile()); 
      	   String message = text.getText(); 
      	   file.write(message, 0, message.length()); 
     	   file.close();
		   JOptionPane.showInternalMessageDialog(null, "File was saved",
		   "information", JOptionPane.INFORMATION_MESSAGE);  	   
     	 }
     	 catch(IOException ex) {     	   
		    JOptionPane.showMessageDialog(null,
    	    ex.getMessage(),
    	    "ERROR",
    	    JOptionPane.ERROR_MESSAGE);
     	 }	   	 
	   }	
     }
     
     if(e.getActionCommand().equals("Close")) {
       System.exit(0);
     }
  }



  public void windowClosing( WindowEvent event ) {
    System.exit( 0 );
  }
  public void windowClosed( WindowEvent event ) {}
  public void windowDeiconified( WindowEvent event ) {}
  public void windowIconified( WindowEvent event ) {}
  public void windowActivated( WindowEvent event ) {}
  public void windowDeactivated( WindowEvent event ) {}
  public void windowOpened( WindowEvent event ) {}

  public static void main(String[] args) {
    JTextEditor window = new JTextEditor();
    window.pack();
    window.setVisible(true);
  }
}
```


----------



## André Uhres (12. Okt 2005)

Das Nummerieren der Zeilen lässt sich am einfachsten über die "Row Header View"
von "JScrollPane" erreichen:

```
scrollpane.setRowHeaderView( new LineNumber( text ) );
```
wobei LineNumber eine Erweiterung von JComponent darstellt welche die
Methode paintComponent(Graphics g) überschreibt, mit etwa folgendem Inhalt:

```
...
                Rectangle drawHere = g.getClipBounds();
                int startLineNumber = (drawHere.y / lineHeight) + 1;
                int endLineNumber = startLineNumber + (drawHere.height / lineHeight);
                
                int start = (drawHere.y / lineHeight) * lineHeight + startOffset;
                
                for (int i = startLineNumber; i <= endLineNumber; i++) {
                    String lineNumber = String.valueOf(i);
                    int stringWidth = fontMetrics.stringWidth( lineNumber );
                    int rowWidth = getSize().width;
                    g.drawString(lineNumber, rowWidth - stringWidth - MARGIN, start);
                    start += lineHeight;
                }
...
```
Um Zeilen einzufärben bietet sich der "DefaultHighlighter.DefaultHighlightPainter"
aus dem "javax.swing.text" package.
Übrigens ist dein Ansatz zum Text Editor nicht mal so schlecht.
Hier noch ein Tip:
Alle TextKomponenten bieten spezielle read()/write() Methoden an, die das Sichern 
und Laden dieser Komponenten erleichtern.


----------



## mangaka (13. Okt 2005)

Hi, 
vielen Dank für die Antwort und für den Tipp mit den Textkomponenten!

Ich versuch mal die Idee umzusetzen 

MFG
mangaka


----------



## André Uhres (13. Okt 2005)

Hier noch nützliche Links:
www.discoverteenergy.com/files/LineNumber.java
javaalmanac.com/egs/javax.swing.text/style_HiliteWords.html


----------



## mangaka (13. Okt 2005)

Ahhh hab schon die ganze Zeit versucht was über den highlighter rauszukriegen, DANKE    

mfg
mangaka

edit:
hmmm ich krieg das nicht hin und verstehn tue ich es sowießo nicht *gg
du hast mir n beispiel geschickt wie ich ein wort markieren könnte, aber ich kann das tut nicht auf mein problem beziehen...

könntest du vllt weiterhelfen?


----------



## André Uhres (13. Okt 2005)

Ich habs!
So wird die Funktion "aktuelle Zeile einfärben" aktiviert:

```
new Line_Highlight(textArea, new Color(255, 255, 220));
```
Und hier die Klasse "Line_Highlight", eine Erweiterung von dem oben genannten "DefaultHighlighter.DefaultHighlightPainter ":

```
/**
 * Line Highlight Class
 */
class Line_Highlight extends
        DefaultHighlighter.DefaultHighlightPainter implements
        CaretListener, MouseListener, MouseMotionListener, KeyListener {
    
    private JTextComponent component;
    private DefaultHighlighter highlighter;
    private Object lastHighlight;
    
    public Line_Highlight(JTextComponent component, Color color) {
        
        super(color);
        this.component = component;
        
        highlighter = (DefaultHighlighter) component.getHighlighter();
        highlighter.setDrawsLayeredHighlights(true);
        
        //  Add listener so we know when to change highlighting
        component.addKeyListener(this);
        component.addCaretListener(this);
        component.addMouseListener(this);
        component.addMouseMotionListener(this);
        
        //  Initially highlight the first line
        addHighlight(0);
    }
    
    public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds,
            JTextComponent c, View view) {
        try {
            
            // Only use the first offset to get the line to highlight
            Rectangle r = c.modelToView(offs0);
            r.x = 0;
            r.width = c.getSize().width;
            
            // --- render ---
            g.setColor(getColor());
            g.fillRect(r.x, r.y, r.width, r.height);
            return r;
        } catch (BadLocationException e) {
            return null;
        }
    }
    
        /*
         * Remove/add the highlight to make sure it gets repainted
         */
    private void resetHighlight() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                highlighter.removeHighlight(lastHighlight);
                
                Element root = component.getDocument()
                .getDefaultRootElement();
                int line = root.getElementIndex(component
                        .getCaretPosition());
                Element lineElement = root.getElement(line);
                int start = lineElement.getStartOffset();
                addHighlight(start);
            }
        });
    }
    
    private void addHighlight(int offset) {
        try {
            lastHighlight = highlighter.addHighlight(offset, offset + 1,
                    this);
        } catch (BadLocationException ble) {
        }
    }
    
    // Removes our private highlights
    public void removeHighlight() {
        highlighter.removeHighlight(lastHighlight);
    }
    
    //  Implement CaretListener
    public void caretUpdate(CaretEvent e) {
        resetHighlight();
    }
    
    //  Implement MouseListener
    public void mousePressed(MouseEvent e) {
        resetHighlight();
    }
    
    public void mouseClicked(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseMoved(MouseEvent e) {}
    public void mouseDragged(MouseEvent e) {
        resetHighlight();
    }
    
    // Implement KeyListener
    public void keyReleased(KeyEvent e) {}
    public void keyPressed(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {}
}
```


----------



## André Uhres (13. Okt 2005)

Beitrag gelöscht


----------



## mangaka (14. Okt 2005)

ohh wie "geil "^^

vielen danke für den code, es funktioniert wunderbar... nur wirds wohl ein bissl dauern bis ich alles was da steht verstanden habe (aller anfang ist schwer  

nochmals vielen dank
mfg mangaka


ps:
ist es in ordnung wenn ich dir ne pm schicke falls ich fragen habe?


----------



## André Uhres (14. Okt 2005)

> ist es in ordnung wenn ich dir ne pm schicke falls ich fragen habe?

Mir ist es lieber wenn alle Fragen im Forum erscheinen. 
Dann haben alle was davon und deine Chancen auf eine gute Antwort 
stehen viel besser. 
Natürlich kannst du mir auch eine pm senden wenn du
deine guten Gründe dafür hast.
Beste Grüsse,
Andre


----------



## André Uhres (14. Okt 2005)

Beitrag gelöscht


----------



## mangaka (15. Okt 2005)

und mich schon vermisst ??

so wie versprochen habe ich ein paar fragen, jedoch immo nur wegen der Line_Highlight klasse. die andere habe ich mir noch nciht angesehn 

ich habe mir nun die klasse angesehn und alles von der idee her verstanden. nur was manche zeilen im einzelnen bedeuten nicht:

1.

```
highlighter = (DefaultHighlighter) component.getHighlighter();
        highlighter.setDrawsLayeredHighlights(true);
```


2.

```
public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds,
            JTextComponent c, View view)
```
so wie ich das verstanden habe, "malt" diese methode die markierung der zeile.
jedoch wird sie, wie ich das sehe,  nie aufgerufen. warum nicht?*naja muss wohl nicht, sonst würd's ja nicht funktionieren 

3.

```
private void resetHighlight() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                highlighter.removeHighlight(lastHighlight);
               
                Element root = component.getDocument()
                .getDefaultRootElement();
                int line = root.getElementIndex(component
                        .getCaretPosition());
                Element lineElement = root.getElement(line);
                int start = lineElement.getStartOffset();
                addHighlight(start);
            }
        });
    }
```
hier wird wohl die letzte markierung gelöscht.
und irgendwann (zeile 13) wieder der aktuellen zeile hinzugefügt.

das was ich jetzt nicht versteh ist was invokelater zu bedeuten hat. in der docu habe ich schon nachgeschaut, aber ich werd nicht schlau drauß, könnt vllt auch an meinen englischkenntnissen liegen^^
wäre sehr nett, wenn du die restlichen zeilen mir auch kurz erklären würdest 


hoffentlich sind das nicht zuviele fragen.
will nämlich gerne alles verstehen können was ich da programmiere.


thx im vorraus

mfg
    mangaka


----------



## André Uhres (16. Okt 2005)

Der "highlighter" ist dafür verantwortlich die "highlights" zu machen

"highlighter.setDrawsLayeredHighlights(true)": 
   bei "true" werden die "highlights" in der Methode "paintLayer" gezeichnet, 
   und zwar bevor der Text gezeichnet wird.

"paintLayer" überschreibt lediglich die entsprechende Methode der Klasse "DefaultHighlightPainter"

"SwingUtilities.invokeLater(...)":
   Die im "Runnable" enthaltene run() Methode wird erst aufgerufen, wenn alle anstehenden AWT Events 
verarbeitet      worden sind (KeyEvent, MouseEvent, CaretEvent, ...).
   Gemäss der Dokumentation sollte die Methode "invokeLater(...)" benutzt werden wenn ein Programm die
   Benutzeroberfläche verändern will, was ja hier der Fall ist.

"Element root = component.getDocument().getDefaultRootElement();"
   Das "Document" enthält die DatenStruktur der "JTextArea". 
   Die Struktur ist hierarchisch gegliedert und geht somit immer von einem "root"-Element aus.

"int line = root.getElementIndex(component.getCaretPosition());"
   Holt die Nummer vom Kind-Element(d.h. der Zeile) das dem angegebenen "offset" am nächsten ist. 
   Der "offset" wird relativ zum Anfang des Dokuments angegeben. Hier ist der "offset" gleich der
   Position des "text insertion caret", also der Position wo Text eingefügt wird.
Kurz: hier wird die aktuelle Zeilennummer geholt.

"Element lineElement = root.getElement(line);"
   Holt das Kind-Element(d.h. die Zeile) mit der angegebenen Nummer.

"int start = lineElement.getStartOffset();"
   Holt die Position bei der dieses Element(d.h. diese Zeile) beginnt, und zwar relativ zum Anfang des Dokuments.


----------



## mangaka (17. Okt 2005)

thx für die antwort... habe alles ungefähr verstanden 

mfg
mangaka


----------

