Source code: com/diaam/lgpl/ts/ShSh.java
1 /*
2 * $Log: ShSh.java,v $
3 * Revision 1.1 2000/11/10 15:31:30 vonarnim
4 * - deplacement (et nouvelle version) de TerminalStandard dans ctulu
5 *
6 * Revision 1.1 1999/11/04 20:02:41 Herve_AGNOUX
7 * Initial revision
8 * License LGPL.
9 * hgnoux@diaam.com
10 */
11 /*
12 -- TerminalStandard v111999
13
14 Fichier : ShSh.java
15 Auteur : Hervé AGNOUX, hagnoux@mail.club-internet.fr
16 License LGPL.
17 */
18
19 package com.diaam.lgpl.ts;
20
21
22 import java.awt.AWTError;
23 import java.awt.AWTEvent;
24 import java.awt.BorderLayout;
25 import java.awt.Dimension;
26 import java.awt.event.*;
27 import java.io.*;
28 import java.util.Enumeration;
29
30 import javax.swing.*;
31 import javax.swing.text.*;
32 import javax.swing.event.CaretListener;
33 import javax.swing.event.CaretEvent;
34 import javax.swing.event.DocumentEvent;
35
36 /**
37 * <code>ShSh</code> est une classe qui a l'ambition de mettre n'importe
38 * quel shell dans swing. <em>L'ambition</em> seulement... Ne me faites
39 * pas dire ce que je n'ai pas dit... A cet effet, une interface générale
40 * est à l'étude, visible dans les packages "script", "realscripts", et
41 * "applications".<br>
42 * ShSh constitue, avec les classes <code>MinReader</code> et
43 * <code>SwingTextDocumentWriter</code>, le pivot du package.<br>
44 * ShSh permet à l'utilisateur de faire... un copier/coller sur
45 * les commandes ! N'est ce pas merveilleux !? Merci, SWING !
46 * Pour la gestion interne, elle utilise la notion d'attribut. Cette
47 * gestion est trés mal faite, mais elle fonctionne.<p>
48 * Quelles sont les évolutions depuis la précédente version ? Pas grand
49 * chose, vu de l'extérieur.
50 * <ul>
51 * <li> les méthodes d'accés aux flux renvoient maintenant des MinReader et
52 * autres SwingTextDocumentWriter, ceci pour accéder à leur méthode
53 * "asStream" pour quand on est obliger les flux d'octets. Avant, elles
54 * renvoyaient simplement des Reader et des Writer.
55 * <li> une grosse modif : au lieu de descendre d'un JTextArea, elle
56 * descend maintenant d'un JTextPane. Cela permet d'utiliser la gestion
57 * des attributs pour la cuisine interne, et d'espérer que dans de futures
58 * versions cette cuisine soit mieux faite.
59 * </ul>
60 * Comment travaille cette classe ? Ce qu'elle reçoit du clavier, elle
61 * le renvoit sur un MinReader ; et lorsque le document associé se modifie,
62 * normalement par l'intermédiaire d'un SwingTextDocumentWriter, ça
63 * s'affiche automatiquement. Ce qui est moins automatique, c'est la gestion
64 * du curseur dans ce cas. Voyez les sources pour constater les jongleries.
65 * <p>Cette classe est en diffusion LGPL, et elle est téléchargeable à
66 * <a href="http://perso.club-internet.fr/hagnoux/java/TerminalStandard">
67 * http://perso.club-internet.fr/hagnoux/java/TerminalStandard</a>.<br>
68 * ... au fait ! Qu'est ce que <big>
69 * <a href="http://www.diaam.com">diaam</a></big> ?
70 * @author <a href="h_agnoux.html">Hervé AGNOUX</a>,
71 * hagnoux@mail.club-internet.fr
72 * @version 111999
73 * @see MinReader
74 * @see SwingTextDocumentWriter
75 */
76 public class ShSh extends javax.swing.JTextPane
77 {
78 private MinReader tubeClavier;
79 private SwingTextDocumentWriter tubeSortieFenêtre;
80 private SimpleAttributeSet attrs;
81
82 /**
83 * Initialise les flux d'entrée/sortie, et un listener pour le caret,
84 * et un listener pour le clavier. Ecoute ce qui se passe sur le document,
85 * et prépare les attributs caractères de façon à pister ceux qui viennent
86 * du clavier.
87 */
88 public ShSh()
89 {
90 this(null);
91 }
92 public ShSh(FileWriter _log)
93 {
94 tubeSortieFenêtre = new SwingTextDocumentWriter(getDocument(), _log);
95 tubeClavier = new MinReader();
96 addCaretListener(new DuCurseur());
97 addKeyListener(new DuClavier());
98 getDocument().addDocumentListener(new DuDocument());
99 attrs = new SimpleAttributeSet();
100 attrs.addAttribute(this, "pas mal !");
101 super.replaceSelection("\n");
102 }
103
104 /**
105 * @return l'équivalent de l'entrée standard.
106 */
107 public MinReader lecteur()
108 {
109 return tubeClavier;
110 }
111
112 /**
113 * @return l'équivalent de la sortie standard.
114 */
115 public SwingTextDocumentWriter rédacteur()
116 {
117 return tubeSortieFenêtre;
118 }
119
120 /**
121 * @return l'équivalent de la sortie erreur standard.
122 */
123 public SwingTextDocumentWriter gueulard()
124 {
125 return tubeSortieFenêtre;
126 }
127
128 /**
129 * En plus du comportement normal, elle envoit la chaîne tapée au
130 * clavier vers le lecteur (entrée standard) à chaque retour chariot.
131 * Normalement, encore une fois, tout ce qui vient du clavier passe par
132 * ici.
133 * @param content le contenu (-> le caractère tapé au clavier, ou le
134 * "collé" si on a fait un "copié/collé").
135 */
136 public void replaceSelection(String content)
137 {
138 try
139 {
140 setCharacterAttributes(attrs, true);
141 super.replaceSelection(content);
142 // on suppose abusivement que 'content' ne contient
143 // qu'un caractère...
144 if (content.charAt(0) == '\n')
145 {
146 int s;
147 int f;
148 String str;
149 int l;
150
151 s = tubeSortieFenêtre.offsetAtteint();
152 f = getDocument().getLength();
153 str = getDocument().getText(s, f - s);
154 l = str.length();
155 for (int i = 0; i < l;i++)
156 tubeClavier.aj(str.charAt(i));
157 }
158 }
159 catch (BadLocationException ble)
160 {
161 throw new AWTError(ble.toString());
162 }
163 }
164
165 class DuCurseur implements CaretListener
166 {
167 public void caretUpdate(CaretEvent e)
168 {
169 if (tubeSortieFenêtre.offsetAtteint() <= e.getDot())
170 {
171 if (!isEditable())
172 setEditable(true);
173 }
174 else
175 {
176 if (isEditable())
177 setEditable(false);
178 }
179 }
180 }
181
182 class DuClavier extends KeyAdapter
183 {
184 public void keyPressed(KeyEvent e)
185 {
186 if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE)
187 {
188 if (tubeSortieFenêtre.offsetAtteint() == getCaret().getDot())
189 {
190 e.consume();
191 }
192 }
193 }
194 }
195
196 private class DuDocument implements javax.swing.event.DocumentListener
197 {
198 public void changedUpdate(DocumentEvent de)
199 {
200 }
201
202 public void insertUpdate(final DocumentEvent de)
203 {
204 final Document d;
205 Runnable r;
206
207 d = de.getDocument();
208 final int i1, i2;
209 final int l;
210 i1 = de.getOffset();
211 i2 = de.getLength();
212 l = d.getLength();
213 if (l == i1 + i2)
214 {
215 Element e1 = d.getDefaultRootElement();
216 Element e2 = e1.getElement(e1.getElementIndex(i1));
217 while (!e2.isLeaf())
218 e2 = e2.getElement(e2.getElementIndex(i1));
219 final Element e3 = e2;
220 AttributeSet as = e3.getAttributes();
221 if (as.isDefined(tubeSortieFenêtre))
222 if (getCaretPosition() != l)
223 {
224 setCaretPosition(l);
225 }
226 }
227 }
228
229 public void removeUpdate(DocumentEvent de)
230 {
231 }
232 }
233
234 /**
235 * Juste pour le test...
236 * @exception Exception R.A.S.
237 */
238 public static void main(String args[]) throws Exception
239 {
240 JFrame jf;
241 JPanel jp;
242 JScrollPane jsp;
243 ShSh ss;
244 Writer stdw;
245 BufferedReader br;
246
247 jf = new JFrame("ShSh");
248 jp = new JPanel();
249 jp.setLayout(new BorderLayout());
250 jp.setPreferredSize(new Dimension(400, 250));
251 ss = new ShSh();
252 jsp = new JScrollPane(ss);
253 jp.add("Center", jsp);
254 jf.setContentPane(jp);
255 jf.pack();
256 jf.addWindowListener(new WindowAdapter()
257 {
258 public void windowClosing(WindowEvent e)
259 {System.exit(0);}
260 });
261 stdw = ss.rédacteur();
262 jf.show();
263 ss.requestFocus();
264 br = new BufferedReader(ss.lecteur());
265 stdw.write("GO !");
266 while (true)
267 {
268 String lu;
269
270 lu = br.readLine();
271 stdw.write(lu);
272 }
273 }
274 }