Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: jaxe/JaxeTextPane.java


1   /*
2   Jaxe - Editeur XML en Java
3   
4   Copyright (C) 2003 Observatoire de Paris
5   
6   Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier conformément aux dispositions de la Licence Publique Générale GNU, telle que publiée par la Free Software Foundation ; version 2 de la licence, ou encore (à votre choix) toute version ultérieure.
7   
8   Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de COMMERCIALISATION ou D'ADAPTATION A UN OBJET PARTICULIER. Pour plus de détail, voir la Licence Publique Générale GNU .
9   
10  Vous devez avoir reçu un exemplaire de la Licence Publique Générale GNU en même temps que ce programme ; si ce n'est pas le cas, écrivez à la Free Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, Etats-Unis.
11  */
12  
13  package jaxe;
14  
15  import jaxe.elements.JETexte;
16  import jaxe.elements.JEStyle;
17  import jaxe.elements.JESwing;
18  
19  import java.awt.*;
20  import java.awt.datatransfer.*;
21  import java.awt.event.*;
22  import java.util.*;
23  import javax.swing.*;
24  import javax.swing.event.*;
25  // attention à ne pas confondre org.w3c.dom.Document avec javax.swing.text.Document
26  import javax.swing.text.*;
27  import javax.swing.undo.*;
28  
29  import org.w3c.dom.Element;
30  import org.w3c.dom.Node;
31  
32  
33  /**
34   * Zone de texte éditable correspondant à un document XML.
35   * Peut être utilisée indépendamment de JaxeFrame et JaxeMenuBar.
36   */
37  public class JaxeTextPane extends JTextPane implements ClipboardOwner {
38      
39      static int cmdMenu;
40      
41      //undo helpers
42      private UndoManager undo = new UndoManager();
43      private boolean ignorerEdition = false;
44      private boolean editionSpeciale = false;
45      private CompoundEdit editSpecial;
46      private int niveauEditionSpeciale = 0;
47      private Stack ignorerEditionStack = new Stack(); // de Boolean
48      
49      private static Object pressePapier = null;
50      private static String ppTexte = null;
51      
52      private String texteRecherche = null;
53      
54      private ArrayList ecouteursArbre = new ArrayList();
55      private ArrayList ecouteursAnnulation = new ArrayList();
56      
57      private JaxeDocument doc;
58      
59      public JFrame jframe;
60      
61      
62      public JaxeTextPane(JaxeDocument doc, JFrame jframe) {
63          super();
64          setEditorKit(doc.createEditorKit());
65          setStyledDocument(doc);
66          this.doc = doc;
67          this.jframe = jframe;
68          doc.setTextPane(this);
69          cmdMenu = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
70          
71          Keymap kmap = getKeymap();
72          KeyStroke cmdx = KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, cmdMenu);
73          kmap.removeKeyStrokeBinding(cmdx);
74          kmap.addActionForKeyStroke(cmdx, new ActionCouper());
75          KeyStroke cmdc = KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, cmdMenu);
76          kmap.removeKeyStrokeBinding(cmdc);
77          kmap.addActionForKeyStroke(cmdc, new ActionCopier());
78          KeyStroke cmdv = KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_V, cmdMenu);
79          kmap.removeKeyStrokeBinding(cmdv);
80          kmap.addActionForKeyStroke(cmdv, new ActionColler());
81  
82          doc.addUndoableEditListener(new MyUndoableEditListener());
83          addCaretListener(new MyCaretListener());
84          
85          setTabs(4);
86      }
87      
88      public UndoManager getUndo() {
89          return(undo);
90      }
91      
92      public void undo() {
93          try {
94              undo.undo();
95          } catch (CannotUndoException ex) {
96              System.out.println(JaxeResourceBundle.getRB().getString("annulation.ImpossibleAnnuler") +
97                  ": " + ex);
98              ex.printStackTrace();
99          }
100         miseAJourAnnulation();
101     }
102     
103     public boolean getEditionSpeciale() {
104         return(editionSpeciale);
105     }
106     
107     public boolean getIgnorerEdition() {
108         return(ignorerEdition);
109     }
110     
111     // inspiré de DefaultEditorKit.CutAction, mais EN FRANCAIS
112     protected class ActionCouper extends TextAction {
113         public ActionCouper() {
114             super(JaxeResourceBundle.getRB().getString("menus.Couper"));
115         }
116 
117         public void actionPerformed(ActionEvent e) {
118             JTextComponent target = getTextComponent(e);
119             if (target instanceof JaxeTextPane)
120                 ((JaxeTextPane)target).couper();
121         }
122     }
123     
124     protected class ActionCopier extends TextAction {
125         public ActionCopier() {
126             super(JaxeResourceBundle.getRB().getString("menus.Copier"));
127         }
128 
129         public void actionPerformed(ActionEvent e) {
130             JTextComponent target = getTextComponent(e);
131             if (target instanceof JaxeTextPane)
132                 ((JaxeTextPane)target).copier();
133         }
134     }
135     
136     protected class ActionColler extends TextAction {
137         public ActionColler() {
138             super(JaxeResourceBundle.getRB().getString("menus.Coller"));
139         }
140 
141         public void actionPerformed(ActionEvent e) {
142             JTextComponent target = getTextComponent(e);
143             if (target instanceof JaxeTextPane)
144                 ((JaxeTextPane)target).coller();
145         }
146     }
147     
148     public void processMouseEvent(MouseEvent e){
149         if(e.isPopupTrigger() && this.isEditable()){
150             showPopup(e);
151         }else{
152           super.processMouseEvent(e);
153         }
154     }
155 
156     private void showPopup(MouseEvent e) {
157         if (e.isPopupTrigger()) {
158             JPopupMenu popup = new JPopupMenu();
159             int pos = viewToModel(e.getPoint());
160             JaxeElement je = doc.elementA(pos);
161             if (je == null)
162                 return;
163             
164             if (je instanceof JETexte)
165                 je = je.getParent();
166             if (pos == je.debut.getOffset() && !(je instanceof JESwing))
167                 je = je.getParent();
168             
169             if (!je.getEditionAutorisee())
170                 return;
171             
172             setCaretPosition(pos);
173             moveCaretPosition(pos);
174             
175             Element parentdef = doc.cfg.getBaliseDef(je.noeud.getNodeName());
176             ArrayList autorisees = doc.cfg.listeSousbalises(parentdef);
177             for (int i=0; i<autorisees.size(); i++) {
178                 String nombalise = (String)autorisees.get(i);
179                 Element balisedef = doc.cfg.getBaliseDef(nombalise);
180                 if (balisedef == null)
181                     ;//System.err.println("Erreur: Impossible de trouver la définition de " + nombalise);
182                 else if (!"style".equals(doc.cfg.typeBalise(balisedef)))
183                     popup.add(new ActionInsertionBalise(doc, balisedef));
184             }
185             
186             if (autorisees.size() > 0) {   // Seperator between elements and Copy'n'Paste
187                 popup.addSeparator();
188             }
189             
190             if (getSelectionEnd() != getSelectionStart()) {  // Copy allowed ?
191                 popup.add(new ActionCouper());
192                 popup.add(new ActionCopier());
193             }
194 
195             popup.add(new ActionColler());
196                 
197             popup.show(e.getComponent(), e.getX(), e.getY());
198         }
199     }
200 
201     
202     public void selectZone(int debut, int fin, boolean select) {
203         ArrayList tel = doc.rootJE.elementsDans(debut, fin-1);
204         if (select) {
205             // on change la sélection pour ne pas inclure des moitié d'éléments (sauf pour le texte)
206             int debut2;
207             int fin2;
208             int ndebut = debut;
209             int nfin = fin;
210             do {
211                 debut2 = ndebut;
212                 fin2 = nfin;
213                 JaxeElement firstel = doc.rootJE.elementA(debut2);
214                 if (firstel instanceof JETexte)
215                     firstel = firstel.getParent();
216                 if (firstel.fin.getOffset() < nfin-1 && !tel.contains(firstel))
217                     ndebut = firstel.fin.getOffset() + 1;
218                 if (firstel.fin.getOffset() == nfin-1 && !tel.contains(firstel)) {
219                     if (!(firstel instanceof JEStyle))
220                         nfin = firstel.fin.getOffset();
221                 }
222                 if (firstel.debut.getOffset() == ndebut && !tel.contains(firstel) &&
223                     !(firstel instanceof JEStyle) && !(firstel instanceof JESwing))
224                     ndebut++;
225                 JaxeElement lastel = doc.rootJE.elementA(fin2);
226                 if (lastel != null && lastel.debut.getOffset() == fin2)
227                     lastel = lastel.getParent();
228                 if (lastel instanceof JETexte)
229                     lastel = lastel.getParent();
230                 if (lastel == null)
231                     nfin = fin2 - 1;
232                 else if (lastel.debut.getOffset() == ndebut && !tel.contains(lastel) &&
233                     !(lastel instanceof JEStyle) && !(lastel instanceof JESwing))
234                     ndebut++;
235                 else if (lastel.debut.getOffset() > ndebut && !tel.contains(lastel))
236                     nfin = lastel.debut.getOffset();
237                 if (nfin < ndebut)
238                     nfin = ndebut;
239             } while (ndebut != debut2 || nfin != fin2);
240             if (ndebut != debut || nfin != fin) {
241                 if (nfin == ndebut)
242                     nfin = ndebut = debut;
243                 setCaretPosition(ndebut);
244                 moveCaretPosition(nfin);
245             }
246             if (ndebut != debut || nfin != fin)
247                 tel = doc.rootJE.elementsDans(ndebut, nfin-1);
248         }
249         for (int i=0; i< tel.size(); i++) {
250             JaxeElement je = (JaxeElement)tel.get(i);
251             je.selection(select);
252         }
253     }
254     
255     /**
256      * Positionne le document à la ligne indiquée (la première ligne a le numéro 1)
257      */
258     public void allerLigne(int ligne) {
259         if (ligne > 0)
260             ligne--;
261         else
262             ligne = 0;
263         int pos = doc.getDefaultRootElement().getElement(ligne).getStartOffset();
264         // bidouille pour afficher la position en haut de la fenêtre
265         try {
266             scrollRectToVisible(modelToView(doc.getLength()));
267             scrollRectToVisible(modelToView(pos));
268         } catch (BadLocationException ex) {
269         }
270     }
271     
272     public void debutIgnorerEdition() {
273         ignorerEdition = true;
274     }
275     
276     public void finIgnorerEdition() {
277         ignorerEdition = false;
278     }
279     
280     class EditSpecial extends CompoundEdit {
281         String titre;
282         public EditSpecial(String titre) {
283             this.titre = titre;
284         }
285         public String getPresentationName() {
286             return(titre);
287         }
288         public String getUndoPresentationName() {
289             return(JaxeResourceBundle.getRB().getString("menus.Annuler") + " " + titre);
290         }
291         public String getRedoPresentationName() {
292             return(JaxeResourceBundle.getRB().getString("menus.Retablir") + " " + titre);
293         }
294     }
295     
296     /**
297      * Edition spéciale: combinaison d'un ensemble de JaxeUndoableEdit.
298      */
299     public void debutEditionSpeciale(String titre, boolean ignorerEdition) {
300         if (niveauEditionSpeciale < 0)
301             System.err.println("Erreur: niveauEditionSpeciale < 0 !");
302         if (niveauEditionSpeciale == 0) {
303             editSpecial = new EditSpecial(titre);
304             editionSpeciale = true;
305             this.ignorerEdition = ignorerEdition;
306         } else {
307             ignorerEditionStack.push(new Boolean(ignorerEdition));
308             this.ignorerEdition = ignorerEdition;
309         }
310         niveauEditionSpeciale += 1;
311     }
312     
313     public void finEditionSpeciale() {
314         niveauEditionSpeciale -= 1;
315         if (niveauEditionSpeciale < 0)
316             System.err.println("Erreur: niveauEditionSpeciale < 0 !");
317         if (niveauEditionSpeciale == 0) {
318             editSpecial.end();
319             undo.addEdit(editSpecial);
320             miseAJourAnnulation();
321             editionSpeciale = false;
322             ignorerEdition = false;
323             editSpecial = null;
324         } else {
325             this.ignorerEdition = ((Boolean)ignorerEditionStack.pop()).booleanValue();
326         }
327     }
328     
329     public void addEdit(UndoableEdit edit) {
330         if (editionSpeciale) {
331             editSpecial.addEdit(edit);
332         } else {
333             getUndo().addEdit(edit);
334             miseAJourAnnulation();
335         }
336     }
337     
338     //This one listens for edits that can be undone.
339     protected class MyUndoableEditListener
340                     implements UndoableEditListener {
341         public void undoableEditHappened(UndoableEditEvent e) {
342             //Remember the edit and update the menus.
343             if (!ignorerEdition) {
344                 undo.addEdit(e.getEdit());
345                 miseAJourAnnulation();
346             }
347         }
348     }
349 
350     public void couper() {
351         int debut = getSelectionStart();
352         int fin = getSelectionEnd();
353         JaxeElement firstel = doc.rootJE.elementA(debut);
354         JaxeElement lastel = doc.rootJE.elementA(fin - 1);
355         if (firstel == lastel && firstel instanceof JETexte) {
356             cut();
357             pressePapier = null;
358             ppTexte = null;
359         } else {
360             Object pp = doc.copier(debut, fin);
361             if (pp != null) {
362                 String s = doc.pp2string(pp);
363                 Clipboard clip = getToolkit().getSystemClipboard();
364                 StringSelection contents = new StringSelection(s);
365                 clip.setContents(contents, this); // va appeler lostOwnership
366                 pressePapier = pp;
367                 ppTexte = s;
368                 try {
369                     doc.remove(debut, fin-debut);
370                 } catch (BadLocationException ex) {
371                     System.err.println("BadLocationException: " + ex.getMessage());
372                 }
373             } else
374                 getToolkit().beep();
375         }
376     }
377     
378     public void copier() {
379         int debut = getSelectionStart();
380         int fin = getSelectionEnd();
381         JaxeElement firstel = doc.rootJE.elementA(debut);
382         JaxeElement lastel = doc.rootJE.elementA(fin - 1);
383         if (firstel == lastel && (firstel instanceof JETexte || firstel instanceof JEStyle)) {
384             copy();
385             pressePapier = null;
386             ppTexte = null;
387         } else {
388             Object pp = doc.copier(debut, fin);
389             if (pp != null) {
390                 String s = doc.pp2string(pp);
391                 Clipboard clip = getToolkit().getSystemClipboard();
392                 StringSelection contents = new StringSelection(s);
393                 clip.setContents(contents, this); // va appeler lostOwnership
394                 pressePapier = pp;
395                 ppTexte = s;
396             } else
397                 getToolkit().beep();
398         }
399     }
400     
401     public void coller() {
402         if (pressePapier != null) {
403             // test si contenu presse-papier = ppTexte
404             Toolkit tk = Toolkit.getDefaultToolkit();
405             Clipboard clip = tk.getSystemClipboard();
406             Transferable trans = clip.getContents(this);
407             if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) {
408                 String spp;
409                 try {
410                     spp = (String)trans.getTransferData(DataFlavor.stringFlavor);
411                 } catch (Exception ex) {
412                     spp = null;
413                 }
414                 if (spp != null && spp.equals(ppTexte)) {
415                     try {
416                         doc.coller(pressePapier, doc.createPosition(getCaretPosition()));
417                     } catch (BadLocationException ex) {
418                         System.err.println("BadLocationException: " + ex.getMessage());
419                     }
420                 } else
421                     doc.coller(this);
422             } else
423                 doc.coller(this);
424         } else
425             doc.coller(this);
426     }
427     
428     public void toutSelectionner() {
429         setCaretPosition(0);
430         moveCaretPosition(doc.getLength());
431     }
432 
433     public void rechercher() {
434         DialogueRechercher dlg = new DialogueRechercher(this);
435         dlg.show();
436         texteRecherche = dlg.getTexteRecherche();
437     }
438 
439     public void rechercher(String s) {
440         texteRecherche = s;
441         int len = texteRecherche.length();
442         int ind = -1;
443         String text;
444         // recherche bourrin
445         try {
446             for (int i=0; i<doc.getLength()-len; i++) {
447                 text = doc.getText(i, len);
448                 if (text.equals(texteRecherche)) {
449                     ind = i;
450                     break;
451                 }
452             }
453         } catch (BadLocationException ex) {
454             System.err.println("BadLocationException: " + ex.getMessage());
455             return;
456         }
457         if (ind != -1) {
458             setCaretPosition(ind);
459             moveCaretPosition(ind+len);
460         } else
461             getToolkit().beep();
462     }
463 
464     public void suivant() {
465         if (texteRecherche == null || texteRecherche.length() == 0)
466             return;
467         int len = texteRecherche.length();
468         int ind = -1;
469         int i0 = getCaretPosition() + 1;
470         String text;
471         // recherche bourrin
472         try {
473             for (int i=i0; i<doc.getLength()-len; i++) {
474                 text = doc.getText(i, len);
475                 if (text.equals(texteRecherche)) {
476                     ind = i;
477                     break;
478                 }
479             }
480         } catch (BadLocationException ex) {
481             System.err.println("BadLocationException: " + ex.getMessage());
482             return;
483         }
484         if (ind != -1) {
485             setCaretPosition(ind);
486             moveCaretPosition(ind+len);
487         } else
488             getToolkit().beep();
489     }
490 
491     public void lostOwnership(Clipboard clipboard, Transferable contents) { // ne marche pas :(
492         pressePapier = null;
493     }
494     
495     public void ajouterEcouteurArbre(EcouteurMAJ ec) {
496         ecouteursArbre.add(ec);
497     }
498     
499     public void retirerEcouteurArbre(EcouteurMAJ ec) {
500         ecouteursArbre.remove(ec);
501     }
502     
503     public void miseAJourArbre() {
504         for (int i=0; i<ecouteursArbre.size(); i++)
505             ((EcouteurMAJ)ecouteursArbre.get(i)).miseAJour();
506     }
507     
508     public void ajouterEcouteurAnnulation(EcouteurMAJ ec) {
509         ecouteursAnnulation.add(ec);
510     }
511     
512     public void retirerEcouteurAnnulation(EcouteurMAJ ec) {
513         ecouteursAnnulation.remove(ec);
514     }
515     
516     public void miseAJourAnnulation() {
517         for (int i=0; i<ecouteursAnnulation.size(); i++)
518             ((EcouteurMAJ)ecouteursAnnulation.get(i)).miseAJour();
519     }
520     
521     //This listens for and reports caret movements.
522     protected class MyCaretListener implements CaretListener {
523         
524         int vdot = 0;
525         int vmark = 0;
526         
527         public void caretUpdate(CaretEvent e) {
528             int dot = e.getDot();
529             int mark = e.getMark();
530             if (dot == mark) {  // no selection
531                 if (vmark - vdot > 0) // on déselectionne
532                     selectZone(vdot, vmark, false);
533             } else { //la  sélection des images du texte n'est pas gérée par Swing !
534                 if (dot > mark) {
535                     dot += mark; // faut pas gâcher les variables
536                     mark = dot - mark;
537                     dot = dot - mark;
538                 }
539                 if (vdot != dot || vmark != mark)
540                     selectZone(vdot, vmark, false);
541                 selectZone(dot, mark, true);
542             }
543             vdot = dot;
544             vmark = mark;
545         }
546     }
547 
548     public void setTabs(int charactersPerTab) {
549         FontMetrics fm = getFontMetrics(getFont());
550         int charWidth = fm.charWidth('w');
551         int tabWidth = charWidth * charactersPerTab;
552         
553         TabStop[] tabs = new TabStop[10];
554         
555         for (int j = 0; j < tabs.length; j++) {
556             int tab = j + 1;
557             tabs[j] = new TabStop( tab * tabWidth );
558         }
559         
560         TabSet tabSet = new TabSet(tabs);
561         SimpleAttributeSet attributes = new SimpleAttributeSet();
562         StyleConstants.setTabSet(attributes, tabSet);
563         int length = doc.getLength();
564         doc.setParagraphAttributes(0, length, attributes, false);
565     }
566 }