5 // Created by Lutz Mueller on 6/11/07.
8 // Copyright (C) 2007 Lutz Mueller
10 // This program is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import java
.awt
.event
.*;
29 import java
.io
.FileReader
;
30 import java
.io
.FileWriter
;
33 import javax
.swing
.text
.*;
34 import javax
.swing
.text
.html
.*;
35 import javax
.swing
.event
.*;
36 import javax
.swing
.undo
.*;
38 @SuppressWarnings("unchecked")
39 public class TextPaneWidget
extends aTextWidget
{
42 String contentType
= null;
44 CaretListener caretListener
;
45 CaretEvent lastCaretEvent
= null;
46 int lastCharCode
= 65535;
47 int lastModifiers
= 0;
48 int documentLength
= 0;
49 String undoState
= "nil";
50 String redoState
= "nil";
52 Color foreground
= new Color(0,0,0);
53 StyledDocument styledDoc
;
55 TextPaneWidget widget
;
57 static final int SYNTAX_NONE
= 0;
58 static final int SYNTAX_NEWLISP
= 1;
59 static final int SYNTAX_C
= 2;
60 static final int SYNTAX_CPP
= 3;
61 static final int SYNTAX_JAVA
= 4;
62 static final int SYNTAX_PHP
= 5;
64 int syntaxSelected
= SYNTAX_NONE
;
67 protected UndoAction undoAction
;
68 protected RedoAction redoAction
;
69 protected UndoManager undo
= new UndoManager();
70 MyUndoableEditListener undoableEditListener
;
71 boolean undoEnabled
= true;
73 public TextPaneWidget(StringTokenizer params
)
75 id
= params
.nextToken();
76 action
= params
.nextToken();
78 textPane
= new JTextPane();
79 textPane
.setDoubleBuffered(true);
81 Caret caret
= new MyCaret();
82 caret
.setBlinkRate(500);
84 textPane
.setCaret(caret
);
86 if(params
.hasMoreTokens())
87 contentType
= params
.nextToken();
88 if(!contentType
.equals("text/html") && !contentType
.equals("text/rtf"))
89 contentType
= "text/plain";
90 textPane
.setContentType(contentType
);
92 areaScrollPane
= new JScrollPane(textPane
);
93 areaScrollPane
.setVerticalScrollBarPolicy(JScrollPane
.VERTICAL_SCROLLBAR_AS_NEEDED
);
95 container
= areaScrollPane
;
96 jcomponent
= textPane
;
97 component
= areaScrollPane
;
103 shTopLevels
= new Vector();
105 if(params
.hasMoreTokens())
106 areaScrollPane
.setPreferredSize(
107 new Dimension (Integer
.parseInt(params
.nextToken()),Integer
.parseInt(params
.nextToken())));
109 gsObject
.widgets
.put(id
, this);
111 styledDoc
= textPane
.getStyledDocument();
112 AbstractDocument doc
= (AbstractDocument
)styledDoc
;
113 //doc.setDocumentFilter(new MyDocumentFilter(1000000));
115 KeyListener keyListener
= new KeyAdapter() {
116 public void keyPressed(KeyEvent e
)
118 Character chr
= new Character(e
.getKeyChar());
119 int code
= e
.getKeyCode();
120 lastCharCode
= chr
.hashCode();
121 int lastPos
, caretPos
, size
, len
;
123 lastModifiers
= e
.getModifiersEx();
125 if(syntaxSelected
!= SYNTAX_NONE
) colorSyntax();
130 highlightClosingPar('(', ')');
133 highlightOpeningPar('(', ')');
136 highlightClosingPar('{', '}');
139 highlightOpeningPar('{', '}');
142 highlightClosingPar('[', ']');
145 highlightOpeningPar('[', ']');
152 caretListener
= new CaretListener() {
153 public void caretUpdate(CaretEvent ce
)
157 if(ce
== null) return;
164 undoAction
.updateUndoState();
165 redoAction
.updateRedoState();
167 if(undoAction
.isEnabled()) undoState
= "true";
168 else undoState
= "nil";
170 if(redoAction
.isEnabled()) redoState
= "true";
171 else redoState
= "nil";
173 guiserver
.out
.println("(" + action
+ " \"" + id
+ "\" " + lastCharCode
+ " " +
174 lastModifiers
+ " " + dot
+ " " + mark
+ " " + documentLength
+ " " +
175 undoState
+ " " + redoState
+ ")");
176 guiserver
.out
.flush();
177 lastCharCode
= 65535;
181 undoAction
= new UndoAction();
182 redoAction
= new RedoAction();
184 undoableEditListener
= new MyUndoableEditListener();
186 styledDoc
.addUndoableEditListener(undoableEditListener
);
187 styledDoc
.addDocumentListener(new MyDocumentListener());
189 // set ctrl-Z and meta-Z undo/redo keys
190 InputMap inputMap
= textPane
.getInputMap();
193 if(guiserver
.MAC_OS_X
)
194 key
= KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Event
.META_MASK
);
196 key
= KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Event
.CTRL_MASK
);
197 inputMap
.put(key
, undoAction
);
199 if(guiserver
.MAC_OS_X
)
200 key
= KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Event
.META_MASK
| Event
.SHIFT_MASK
);
202 key
= KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Event
.CTRL_MASK
| Event
.SHIFT_MASK
);
203 inputMap
.put(key
, redoAction
);
205 textPane
.addHyperlinkListener(new Hyperactive());
206 textPane
.addKeyListener(keyListener
);
207 textPane
.addCaretListener(caretListener
);
210 public void highlightOpeningPar(char opng
, char clsng
)
213 String text
= textPane
.getText();
214 int currentPos
= textPane
.getCaretPosition();
216 int len
= text
.length();
218 caretPos
= currentPos
- 1; // new
219 // go back looking for matching parenthesis
220 while(balance
!= 0 && caretPos
>= 0)
222 if(text
.charAt(caretPos
) == opng
) ++balance
;
223 else if(text
.charAt(caretPos
) == clsng
) --balance
;
229 if(balance
== 0) highlightPosition(caretPos
, currentPos
);
232 public void highlightClosingPar(char opng
, char clsng
)
235 String text
= textPane
.getText();
236 int currentPos
= textPane
.getCaretPosition();
238 int len
= text
.length();
242 caretPos
= currentPos
;
244 // go forward to matching parentheseis
245 while(balance
!= 0 && caretPos
< len
)
247 if(text
.charAt(caretPos
) == opng
) ++balance
;
248 else if(text
.charAt(caretPos
) == clsng
) --balance
;
252 if(balance
== 0) highlightPosition(caretPos
, currentPos
);
255 public void highlightPosition(int pos
, int oldPos
)
257 textPane
.setCaretPosition(pos
);
258 textPane
.getCaret().paint(textPane
.getGraphics());
260 try {Thread
.sleep(400); } catch (InterruptedException ie
) {}
261 textPane
.setCaretPosition(oldPos
);
262 textPane
.getCaret().paint(textPane
.getGraphics());
266 public void loadText(StringTokenizer tokens
)
268 String path
= Base64Coder
.decodeString(tokens
.nextToken());
270 EditorKit kit
= textPane
.getEditorKit();
272 System
.setProperty("line.separator", "\n");
276 kit
.read(new FilterFileReader(path
, "UTF8"), styledDoc
, 0);
278 kit
.read(new FilterFileReader(path
), styledDoc
, 0);
280 catch(Exception ex
) { ErrorDialog
.show("gs:load-text", "Cannot load or decode file: " + path
); }
284 public void saveText(StringTokenizer tokens
)
286 String path
= Base64Coder
.decodeString(tokens
.nextToken());
288 EditorKit kit
= textPane
.getEditorKit();
290 try { kit
.write(new FileWriter(path
), styledDoc
, 0, styledDoc
.getLength()); }
291 catch(Exception ex
) { ErrorDialog
.show("gs:save-text", "Cannot save file: " + path
); }
295 public void findText(StringTokenizer tokens
)
297 String findtext
= Base64Coder
.decodeString(tokens
.nextToken());
298 String direction
= "next";
299 boolean isRegex
= false;
302 String findTextAction
= tokens
.nextToken();
304 if(tokens
.hasMoreTokens())
305 direction
= tokens
.nextToken();
307 if(tokens
.hasMoreTokens())
308 isRegex
= tokens
.nextToken().equals("true");
310 String text
= textPane
.getText();
312 int currentCaret
= textPane
.getCaretPosition();
314 if(direction
.equals("previous"))
317 if(currentCaret
> findtext
.length() + 1)
318 currentCaret
-= findtext
.length() + 1;
319 dot
= text
.lastIndexOf(findtext
, currentCaret
);
322 dot
= text
.indexOf(findtext
, currentCaret
);
324 guiserver
.out
.println("(" + findTextAction
+ " \"" + id
+ "\" " + dot
+ ")");
325 guiserver
.out
.flush();
329 textPane
.setCaretPosition(currentCaret
);
333 textPane
.setCaretPosition(dot
);
334 textPane
.moveCaretPosition(dot
+ findtext
.length());
337 public void setTabSize(StringTokenizer tokens
)
339 int tabSize
= Integer
.parseInt(tokens
.nextToken());
341 TabSet tabSet
= new TabSet(new TabStop
[] {
342 new TabStop(tabSize
), new TabStop(2 * tabSize
), new TabStop(3 * tabSize
),
343 new TabStop(4 * tabSize
), new TabStop(5 * tabSize
), new TabStop(6 * tabSize
),
344 new TabStop(7 * tabSize
), new TabStop(8 * tabSize
), new TabStop(9 * tabSize
) });
346 SimpleAttributeSet attributes
= new SimpleAttributeSet();
347 StyleConstants
.setTabSet(attributes
, tabSet
);
348 styledDoc
.setParagraphAttributes(0, documentLength
, attributes
, false);
351 public void setSyntax(StringTokenizer tokens
)
353 String tkn
= tokens
.nextToken();
355 if(tkn
.equals("true") || tkn
.equals("lsp"))
356 syntaxSelected
= SYNTAX_NEWLISP
;
357 else if(tkn
.equals("c"))
358 syntaxSelected
= SYNTAX_C
;
359 else if(tkn
.equals("cpp"))
360 syntaxSelected
= SYNTAX_CPP
;
361 else if(tkn
.equals("java"))
362 syntaxSelected
= SYNTAX_JAVA
;
363 else if(tkn
.equals("php"))
364 syntaxSelected
= SYNTAX_PHP
;
365 else syntaxSelected
= SYNTAX_NONE
;
367 //textPane.setCursor(new Cursor(Cursor.WAIT_CURSOR));
368 if(syntaxSelected
== SYNTAX_NONE
)
370 SimpleAttributeSet normal
= new SimpleAttributeSet();
371 StyleConstants
.setForeground(normal
, widget
.foreground
);
372 styledDoc
.setCharacterAttributes(0, documentLength
, normal
, true);
376 shTopLevels
.removeAllElements();
377 if(syntaxSelected
== SYNTAX_NEWLISP
)
378 SyntaxHighlighter
.color(widget
, 0, documentLength
);
381 SyntaxHighlighterC
.setFlavor(syntaxSelected
);
382 SyntaxHighlighterC
.color(widget
, 0, documentLength
);
385 //textPane.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
387 undo
.discardAllEdits();
390 public void setForeground(StringTokenizer tokens
)
392 Float red
= Float
.parseFloat(tokens
.nextToken());
393 Float green
= Float
.parseFloat(tokens
.nextToken());
394 Float blue
= Float
.parseFloat(tokens
.nextToken());
395 if(tokens
.hasMoreTokens())
397 Float alpha
= Float
.parseFloat(tokens
.nextToken());
398 textPane
.setForeground(new Color(red
, green
, blue
, alpha
));
401 textPane
.setForeground(new Color(red
, green
, blue
));
403 widget
.foreground
= textPane
.getForeground();
406 class Hyperactive
implements HyperlinkListener
{
407 public void hyperlinkUpdate(HyperlinkEvent e
) {
408 if (e
.getEventType() == HyperlinkEvent
.EventType
.ACTIVATED
) {
409 JEditorPane pane
= (JEditorPane
) e
.getSource();
410 if (e
instanceof HTMLFrameHyperlinkEvent
) {
411 HTMLFrameHyperlinkEvent evt
= (HTMLFrameHyperlinkEvent
)e
;
412 HTMLDocument doc
= (HTMLDocument
)pane
.getDocument();
413 doc
.processHTMLFrameHyperlinkEvent(evt
);
416 pane
.setPage(e
.getURL());
417 } catch (Throwable t
) {
418 textPane
.setText("The page could not be displayed.");
427 public void undoEnable(StringTokenizer tokens
)
429 undoEnabled
= tokens
.nextToken().equals("true");
432 public void undoText(StringTokenizer tokens
)
434 undoAction
.actionPerformed(new ActionEvent(textPane
, Event
.ACTION_EVENT
, "Undo"));
438 public void redoText(StringTokenizer tokens
)
440 redoAction
.actionPerformed(new ActionEvent(textPane
, Event
.ACTION_EVENT
, "Redo"));
443 public void colorSyntax()
445 int caretPos
= textPane
.getCaretPosition();
447 int size
= shTopLevels
.size();
450 for(int idx
= size
- 1; idx
>= 0; idx
--)
452 pos
= (Integer
)shTopLevels
.elementAt(idx
);
455 for(int i
= idx
; i
< size
; i
++)
457 p
= (Integer
)shTopLevels
.elementAt(idx
);
458 shTopLevels
.removeElementAt(idx
);
464 if(documentLength
> caretPos
+ 1024)
465 len
= caretPos
+ 1024;
467 len
= documentLength
;
469 if(syntaxSelected
== SYNTAX_NEWLISP
)
470 SyntaxHighlighter
.color(widget
, pos
, len
);
472 SyntaxHighlighterC
.color(widget
, pos
, len
);
475 //This one listens for edits that can be undone.
476 protected class MyUndoableEditListener
implements UndoableEditListener
478 public void undoableEditHappened(UndoableEditEvent e
)
480 //Remember the edit and update the menus.
481 if(SyntaxHighlighter
.active
|| undoEnabled
!= true) return;
482 undo
.addEdit(e
.getEdit());
483 //System.out.println("=>" + undo.getUndoPresentationName());
484 undoAction
.updateUndoState();
485 redoAction
.updateRedoState();
489 // Listens for any changes to the document.
490 protected class MyDocumentListener
implements DocumentListener
492 public void insertUpdate(DocumentEvent e
) { updateParams(e
);}
493 public void removeUpdate(DocumentEvent e
) { updateParams(e
);}
494 public void changedUpdate(DocumentEvent e
) { updateParams(e
);}
496 private void updateParams(DocumentEvent e
)
498 Document document
= (Document
)e
.getDocument();
499 documentLength
= document
.getLength();
503 class UndoAction
extends AbstractAction
505 public UndoAction() {
510 public void actionPerformed(ActionEvent e
)
512 String pname
= undo
.getUndoPresentationName();
513 try { undo
.undo(); } catch (Exception ex
)
515 Toolkit
.getDefaultToolkit().beep();
518 String qname
= undo
.getUndoPresentationName();
519 if(pname
.equals("Undo addition") && qname
.equals("Undo deletion"))
521 try { undo
.undo(); } catch (Exception ex
)
523 Toolkit
.getDefaultToolkit().beep();
528 redoAction
.updateRedoState();
529 caretListener
.caretUpdate(lastCaretEvent
);
532 protected void updateUndoState() {
533 if (undo
.canUndo()) {
535 putValue(Action
.NAME
, undo
.getUndoPresentationName());
539 putValue(Action
.NAME
, "Undo");
544 class RedoAction
extends AbstractAction
546 public RedoAction() {
551 public void actionPerformed(ActionEvent e
)
553 String pname
= undo
.getRedoPresentationName();
554 try { undo
.redo();} catch (Exception ex
)
556 Toolkit
.getDefaultToolkit().beep();
559 String qname
= undo
.getRedoPresentationName();
560 if(pname
.equals("Redo deletion") && qname
.equals("Redo addition"))
562 try { undo
.redo(); } catch (Exception ex
)
564 Toolkit
.getDefaultToolkit().beep();
569 undoAction
.updateUndoState();
570 caretListener
.caretUpdate(lastCaretEvent
);
573 protected void updateRedoState()
575 if (undo
.canRedo()) {
577 putValue(Action
.NAME
, undo
.getRedoPresentationName());
581 putValue(Action
.NAME
, "Redo");
586 class MyCaret
extends DefaultCaret
588 public void paint(Graphics g
) {
589 if (!isVisible()) return;
592 JTextComponent c
= getComponent();
594 Rectangle r
= c
.modelToView(dot
);
595 g
.setColor(c
.getCaretColor());
596 g
.fillRect(r
.x
, r
.y
, 2, r
.height
);
598 catch (Exception e
) { System
.err
.println("."); }
601 protected synchronized void damage(Rectangle r
)
603 if (r
== null) return;