Source code: jaxe/JaxeElement.java
1 /*
2 Jaxe - Editeur XML en Java
3
4 Copyright (C) 2002 Observatoire de Paris-Meudon
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.JEInconnu;
16 import jaxe.elements.JEStyle;
17 import jaxe.elements.JESwing;
18 import jaxe.elements.JETableTexte;
19 import jaxe.elements.JETexte;
20
21 import java.awt.*;
22 import java.util.ArrayList;
23 import java.awt.event.MouseAdapter;
24 import java.awt.event.MouseEvent;
25 import javax.swing.*;
26 import javax.swing.text.AttributeSet;
27 import javax.swing.text.BadLocationException;
28 import javax.swing.text.Position;
29 import javax.swing.text.SimpleAttributeSet;
30 import javax.swing.text.Style;
31 import javax.swing.text.StyleConstants;
32 import org.w3c.dom.Element;
33 import org.w3c.dom.Node;
34 import org.w3c.dom.DOMException;
35
36 /**
37 * Elément Jaxe, représentant à la fois l'affichage graphique et l'arbre DOM correspondant (noeud)
38 */
39 public abstract class JaxeElement {
40
41 public final static String kNormal = "NORMAL";
42 public final static String kGras = "GRAS";
43 public final static String kItalique = "ITALIQUE";
44 public final static String kExposant = "EXPOSANT";
45 public final static String kIndice = "INDICE";
46 public final static String kSouligne = "SOULIGNE";
47
48 //static String newline = Jaxe.newline;
49 public Position debut=null; // position du premier caractère de l'élément
50 public Position fin=null; // position du dernier caractère de l'élément
51 public Node noeud;
52 public JaxeDocument doc;
53 ArrayList jcomps = new ArrayList(); // de JComponent
54 ArrayList compos = new ArrayList(); // de Position (positions des composants)
55 private boolean effacementAutorise = true;
56 private boolean editionAutorisee = true;
57
58 /**
59 * Insère le texte de l'élément à partir de l'arbre DOM, à la position pos dans le texte
60 */
61 public abstract void init(Position pos, Node noeud);
62
63 /**
64 * Initialise le champ noeud, met à jour dom2JaxeElement, et appelle init(pos, noeud)
65 */
66 public void creer(Position pos, Node noeud) {
67 this.noeud = noeud;
68 doc.dom2JaxeElement.put(noeud, this);
69 init(pos, noeud);
70 Element defbalise = null;
71 if (doc.cfg != null) {
72 defbalise = doc.cfg.getBaliseDef(noeud.getNodeName());
73 if (defbalise != null) {
74 String seffacement = doc.cfg.getParamFromDefinition(defbalise, "effacementAutorise", null);
75 String sedition = doc.cfg.getParamFromDefinition(defbalise, "editionAutorisee", null);
76 effacementAutorise = !("false".equals(seffacement));
77 editionAutorisee = !("false".equals(sedition));
78 }
79 }
80 }
81
82 public abstract Element nouvelElement(Element defbalise);
83
84 /**
85 * Affiche le dialogue correspondant à l'élément
86 */
87 public void afficherDialogue(JFrame jframe) {
88 // à remplacer dans les sousclasses
89 }
90
91 /**
92 * Mise à jour de l'affichage par rapport à l'arbre XML
93 */
94 public void majAffichage() {
95 // à remplacer dans les sousclasses
96 }
97
98 /**
99 * Test et mise à jour de l'affichage de la validité
100 */
101 public void majValidite() {
102 // à remplacer dans les sousclasses
103 }
104
105 /**
106 * Renvoit la liste des composants graphiques utilisés dans l'affichage en plus du texte
107 */
108 public ArrayList getComponents() {
109 return jcomps;
110 }
111
112 /**
113 * Renvoit la liste des positions dans le texte des composants graphiques
114 */
115 public ArrayList getComponentPositions() {
116 return compos;
117 }
118
119 /**
120 * Insère le texte dans le Textpane en mettant à jour debut et fin
121 */
122 public Position insertText(Position pos, String texte, AttributeSet attset) {
123 try {
124 int offsetdebut = pos.getOffset();
125 doc.insertString(pos.getOffset(), texte, attset);
126 if (debut == null)
127 debut = doc.createPosition(offsetdebut);
128 if (pos.getOffset() == 0) // bug fix with insertString
129 pos = doc.createPosition(1);
130 fin = doc.createPosition(pos.getOffset()-1);
131 } catch (BadLocationException ex) {
132 System.err.println("BadLocationException: " + ex.getMessage());
133 }
134 return(pos);
135 }
136
137 /**
138 * Insère le texte dans le Textpane en mettant à jour debut et fin
139 */
140 public Position insertText(Position pos, String texte) {
141 SimpleAttributeSet att = null;
142 JaxeElement jeparent = doc.elementA(pos.getOffset());
143 if (jeparent != null) {
144 if (jeparent.debut.getOffset() == pos.getOffset() && !(jeparent instanceof JESwing))
145 jeparent = jeparent.getParent();
146 att = jeparent.attStyle(null);
147 }
148 return(insertText(pos, texte, att));
149 }
150
151 /**
152 * Insère le composant graphique dans le texte, en l'ajoutant dans la liste des composants
153 * et en mettant à jour debut et fin
154 */
155 public Position insertComponent(Position pos, JComponent comp) {
156 int offsetdebut = pos.getOffset();
157 Style s = doc.textPane.addStyle(null, null);
158 StyleConstants.setComponent(s, comp);
159 try {
160 doc.insertString(pos.getOffset(), "*", s);
161 jcomps.add(comp);
162 compos.add(doc.createPosition(pos.getOffset() - 1));
163 doc.fixbug(comp);
164 if (debut == null)
165 debut = doc.createPosition(offsetdebut);
166 fin = doc.createPosition(offsetdebut);
167 if (pos.getOffset() == 0) // bug fix with insertString
168 pos = doc.createPosition(1);
169 } catch (BadLocationException ex) {
170 System.err.println("BadLocationException: " + ex.getMessage());
171 }
172 return(pos);
173 }
174
175 /**
176 * Insère l'icône dans le texte, en l'ajoutant dans la liste des composants
177 * et en mettant à jour debut et fin
178 */
179 public Position insertIcon(Position pos, Icon icon) {
180 int offsetdebut = pos.getOffset();
181 Style s = doc.textPane.addStyle(null, null);
182 StyleConstants.setIcon(s, icon);
183 try {
184 doc.insertString(pos.getOffset(), "*", s);
185 jcomps.add(icon);
186 compos.add(doc.createPosition(pos.getOffset() - 1));
187 //doc.fixbug(comp);
188 if (debut == null)
189 debut = doc.createPosition(offsetdebut);
190 fin = doc.createPosition(offsetdebut);
191 if (pos.getOffset() == 0) // bug fix with insertString
192 pos = doc.createPosition(1);
193 } catch (BadLocationException ex) {
194 System.err.println("BadLocationException: " + ex.getMessage());
195 }
196 return(pos);
197 }
198
199 /**
200 * Renvoit l'élément de plus bas niveau se trouvant à la position donnée dans le texte
201 */
202 public JaxeElement elementA(int pos) {
203 if (debut == null || fin == null)
204 return null;
205 if (debut.getOffset() > pos || fin.getOffset() < pos)
206 return null;
207 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
208 if (n.getNodeType() == Node.ELEMENT_NODE || n.getNodeType() == Node.TEXT_NODE) {
209 JaxeElement je = doc.getElementForNode(n);
210 if (je != null) {
211 JaxeElement nje = je.elementA(pos);
212 if (nje != null)
213 return nje;
214 }
215 }
216 }
217 return this;
218 }
219
220 /**
221 * Renvoit les éléments se trouvant dans la zone du texte indiquée
222 */
223 public ArrayList elementsDans(int dpos, int fpos) {
224 ArrayList l = new ArrayList();
225 if (debut == null || fin == null)
226 return l;
227 if (debut.getOffset() > fpos || fin.getOffset() < dpos)
228 return l;
229 if (debut.getOffset() >= dpos && fin.getOffset() <= fpos)
230 l.add(this);
231 else
232 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
233 if (n.getNodeType() == Node.ELEMENT_NODE || n.getNodeType() == Node.TEXT_NODE) {
234 JaxeElement je = doc.getElementForNode(n);
235 if (je != null)
236 l.addAll(je.elementsDans(dpos, fpos));
237 }
238 }
239 return l;
240 }
241
242 /**
243 * Renvoit le nombre XPath (le numéro de l'élément dans la liste des éléments
244 * avec ce nom), ou 0 si le noeud n'a pas de parent.
245 */
246 public int nombreXPath() {
247 JaxeElement p = getParent();
248 if (p == null)
249 return(0);
250 int no = 0;
251 String nomel = noeud.getNodeName();
252 for (Node n=p.noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
253 if (nomel.equals(n.getNodeName()))
254 no++;
255 if (n == noeud)
256 break;
257 }
258 return(no);
259 }
260
261 /**
262 * Renvoit le chemin XML pour la position pos
263 */
264 public String cheminA(int pos) {
265 if (debut == null || fin == null)
266 return null;
267 if (debut.getOffset() > pos || fin.getOffset() < pos)
268 return null;
269 if (noeud.getNodeType() == Node.TEXT_NODE)
270 return("texte");
271 if (!(noeud.getNodeType() == Node.ELEMENT_NODE))
272 return(null);
273 Element el = (Element)noeud;
274 String nomel = el.getTagName();
275 if (getParent() != null)
276 nomel += "[" + nombreXPath() + "]";
277 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
278 if (n.getNodeType() == Node.ELEMENT_NODE || n.getNodeType() == Node.TEXT_NODE) {
279 JaxeElement je = doc.getElementForNode(n);
280 if (je != null) {
281 String chemin = je.cheminA(pos);
282 if (chemin != null)
283 return(nomel + "/" + chemin);
284 }
285 }
286 }
287 return(nomel);
288 }
289
290 /**
291 * Renvoit le premier élément enfant de celui-ci dont la position est pos ou après pos
292 */
293 public JaxeElement enfantApres(int pos) {
294 if (debut == null || fin == null)
295 return null;
296 if (debut.getOffset() > pos || fin.getOffset() < pos)
297 return null;
298 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
299 if (n.getNodeType() == Node.ELEMENT_NODE || n.getNodeType() == Node.TEXT_NODE) {
300 JaxeElement je = doc.getElementForNode(n);
301 if (je != null) {
302 if (je.debut.getOffset() == pos)
303 return(je);
304 JaxeElement nje = je.elementA(pos);
305 if (nje != null && n.getNextSibling() != null) {
306 return(doc.getElementForNode(n.getNextSibling()));
307 }
308 }
309 }
310 }
311 return null;
312 }
313
314 /**
315 * appelé juste avant que l'élément soit effacé
316 */
317 public void effacer() {
318 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
319 if (n.getNodeType() == Node.ELEMENT_NODE || n.getNodeType() == Node.TEXT_NODE) {
320 JaxeElement je = doc.getElementForNode(n);
321 if (je != null)
322 je.effacer();
323 }
324 }
325 jcomps = new ArrayList();
326 compos = new ArrayList();
327 }
328
329 /**
330 * met à jour l'arbre de JaxeElement et l'arbre DOM à partir de modifs de la zone de texte
331 */
332 public void mettreAJourDOM() {
333 if (debut == null || fin == null)
334 return;
335 try {
336 if (noeud.getNodeType() == Node.TEXT_NODE || this instanceof JEStyle) {
337 Node nsuivant = noeud.getNextSibling();
338 if (nsuivant != null) {
339 JaxeElement jesuivant = doc.getElementForNode(nsuivant);
340 if (jesuivant != null && jesuivant.debut.getOffset() > fin.getOffset() + 1) {
341 // texte rajouté à la fin, avant un autre élément
342 fin = doc.createPosition(jesuivant.debut.getOffset() - 1);
343 }
344 }
345 String texte = doc.getText(debut.getOffset(), fin.getOffset() - debut.getOffset() + 1);
346 if (texte == null || "".equals(texte))
347 getParent().supprimerEnfant(this);
348 else {
349 if (noeud.getNodeType() == Node.TEXT_NODE)
350 noeud.setNodeValue(texte);
351 else if (noeud.getFirstChild() != null) //jestyle
352 noeud.getFirstChild().setNodeValue(texte);
353 }
354 } else {
355 int offdebut = debut.getOffset();
356 int debuttexte;
357 if (this instanceof JESwing)
358 debuttexte = offdebut;
359 else
360 debuttexte = offdebut + 1;
361 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
362 JaxeElement je = doc.getElementForNode(n);
363 if (je != null) {
364 if (debuttexte < je.debut.getOffset()) {
365 JaxeElement jeprev = null;
366 if (n.getPreviousSibling() != null)
367 jeprev = doc.getElementForNode(n.getPreviousSibling());
368 if (jeprev != null &&
369 (jeprev instanceof JEStyle || jeprev instanceof JETexte)) {
370 // texte ajouté à la fin du précédent noeud
371 jeprev.fin = doc.createPosition(je.debut.getOffset() - 1);
372 } else if (je instanceof JETexte)
373 // texte ajouté au début
374 je.debut = doc.createPosition(debuttexte);
375 else {
376 // nouvelle zone de texte avant ce noeud
377 String texte = doc.getText(debuttexte,
378 je.debut.getOffset() - debuttexte);
379 JETexte newje = JETexte.nouveau(doc, doc.createPosition(debuttexte),
380 doc.createPosition(je.debut.getOffset() - 1), texte);
381 noeud.insertBefore(newje.noeud, n);
382 }
383 }
384 offdebut = je.fin.getOffset();
385 debuttexte = offdebut + 1;
386 }
387 }
388 if (debuttexte < fin.getOffset()) { // texte à la fin, après le dernier enfant
389 JaxeElement pje = null;
390 if (noeud.getLastChild() != null)
391 pje = doc.getElementForNode(noeud.getLastChild());
392 if (pje instanceof JEStyle || pje instanceof JETexte)
393 // texte ajouté à la fin du dernier enfant
394 pje.fin = doc.createPosition(fin.getOffset() - 1);
395 else {
396 // nouvelle zone de texte à la fin
397 String texte = doc.getText(debuttexte,
398 fin.getOffset() - debuttexte);
399 JETexte newje = JETexte.nouveau(doc, doc.createPosition(debuttexte),
400 doc.createPosition(fin.getOffset() - 1), texte);
401 noeud.appendChild(newje.noeud);
402 }
403 }
404 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
405 JaxeElement je = doc.getElementForNode(n);
406 if (je != null)
407 je.mettreAJourDOM();
408 }
409 }
410 } catch (BadLocationException ex) {
411 System.err.println("mettreAJourDOM: BadLocationException: " + ex.getMessage());
412 }
413 }
414
415 /**
416 * nouvel élément DOM
417 */
418 public static Element nouvelElementDOM(JaxeDocument doc, String nombalise) {
419 Element newel;
420 if (doc.cfg.namespace() == null)
421 newel = doc.DOMdoc.createElement(nombalise);
422 else {
423 Config conf = doc.cfg.getBaliseConf(nombalise);
424 if (conf == null)
425 conf = doc.cfg;
426 if (conf.prefixe() != null)
427 nombalise = conf.prefixe() + ":" + nombalise;
428 newel = doc.DOMdoc.createElementNS(conf.namespace(), nombalise);
429 }
430 return(newel);
431 }
432
433 /**
434 * initialise et insère cet élément dans le texte et l'arbre DOM
435 */
436 public void inserer(Position pos, Node newel) {
437 doc.textPane.debutIgnorerEdition();
438 insererDOM(pos, newel);
439 creer(pos, newel);
440 doc.textPane.finIgnorerEdition();
441
442 // JESwing: mise à jour du début des parents
443 JaxeElement jeparent = getParent();
444 while (jeparent instanceof JESwing && jeparent.debut.getOffset() > debut.getOffset()) {
445 jeparent.debut = debut;
446 jeparent = jeparent.getParent();
447 }
448 }
449
450 /**
451 * insère newel dans l'arbre DOM
452 */
453 public void insererDOM(Position pos, Node newel) {
454 JaxeElement parent = doc.rootJE.elementA(pos.getOffset());
455 if (parent.debut.getOffset() == pos.getOffset() && !(parent instanceof JESwing))
456 parent = parent.getParent();
457 if (parent instanceof JETexte) {
458 int ic = pos.getOffset() - parent.debut.getOffset();
459 if (ic > 0) {
460 // nouvelle zone de texte... à revoir
461 /*String s = parent.noeud.getNodeValue();
462 String s1 = s.substring(0, ic);
463 String s2 = s.substring(ic);
464 parent.noeud.setNodeValue(s2);
465 Node ns1 = doc.DOMdoc.createTextNode(s1);
466 Node parent2 = parent.noeud.getParentNode();
467 parent2.insertBefore(ns1, parent.noeud);
468 parent2.insertBefore(newel, parent.noeud);*/
469 JaxeElement je2 = parent.couper(pos);
470 Node parent2 = parent.noeud.getParentNode();
471 parent2.insertBefore(newel, je2.noeud);
472 } else {
473 Node parent2 = parent.noeud.getParentNode();
474 parent2.insertBefore(newel, parent.noeud);
475 }
476 } else {
477 JaxeElement jelbef = parent.enfantApres(pos.getOffset());
478 if (jelbef == null)
479 parent.noeud.appendChild(newel);
480 else
481 parent.noeud.insertBefore(newel, jelbef.noeud);
482 }
483 }
484
485 /**
486 * creer les enfants de ce noeud, en supposant que c'est un élément DOM
487 */
488 public void creerEnfants(Position newpos) {
489 Element el = (Element)noeud;
490 for (Node n=el.getFirstChild(); n != null; n=n.getNextSibling())
491 creerEnfant(newpos, n);
492 }
493
494 /**
495 * creer l'enfant n à la position newpos (avec JaxeElement.creer)
496 */
497 public void creerEnfant(Position newpos, Node n) {
498 int offsetdebut = newpos.getOffset();
499 if (n.getNodeType() == Node.ELEMENT_NODE) {
500 String balise = n.getNodeName();
501 Element bdef = null;
502 if (doc.cfg != null)
503 bdef = doc.cfg.getBaliseDef(balise);
504 if (bdef == null) {
505 JEInconnu newje = new JEInconnu(doc);
506 newje.creer(newpos, (Element)n);
507 } else {
508 String typebalise = bdef.getAttribute("type");
509 if (typebalise.equals("style") && n.getChildNodes().getLength() == 0) {
510 // on ne crée pas de JEStyle vide, sinon debut = fin = null -> pb
511 } else {
512 JaxeElement newje;
513 JaxeElement oldje = doc.getElementForNode(n);
514 if (oldje != null) {
515 // il existe déjà un JaxeElement pour ce noeud, on va le réutiliser
516 // (il est peut-être pointé par un JaxeUndoableEdit)
517 newje = oldje;
518 newje.debut = null;
519 newje.fin = null;
520 newje.jcomps = new ArrayList();
521 newje.compos = new ArrayList();
522 } else
523 newje = JEFactory.createJE(typebalise, doc, bdef, (Element)n);
524 newje.creer(newpos, (Element)n);
525 }
526 }
527 } else if (n.getNodeType() == Node.TEXT_NODE) {
528 JETexte newje = new JETexte(doc);
529 newje.creer(newpos, n);
530 }
531 try {
532 if (debut == null)
533 debut = doc.createPosition(offsetdebut);
534 if (newpos.getOffset() == 0) // bug fix with insertString
535 newpos = doc.createPosition(1);
536 fin = doc.createPosition(newpos.getOffset()-1);
537 } catch (BadLocationException ex) {
538 System.err.println("BadLocationException: " + ex.getMessage());
539 }
540 }
541
542 /**
543 * position pour setCaretPosition après création d'un nouvel élément
544 */
545 public Position insPosition() {
546 try {
547 Position p = doc.createPosition(fin.getOffset() + 1);
548 return(p);
549 } catch (BadLocationException ex) {
550 System.err.println("BadLocationException: " + ex.getMessage());
551 return(debut);
552 }
553 }
554
555 /**
556 * Renvoit l'élément parent, en utilisant l'arbre DOM
557 */
558 public JaxeElement getParent() {
559 Node parent = noeud.getParentNode();
560 if (parent == null)
561 return null;
562 return(doc.getElementForNode(parent));
563 }
564
565 /**
566 * supprime l'enfant je à la fois dans le texte et dans le DOM
567 */
568 public void supprimerEnfant(JaxeElement je) {
569 try {
570 int len = je.fin.getOffset() - je.debut.getOffset() + 1;
571 /*String cfin = doc.getText(je.fin.getOffset() + 1, 1);
572 if (newline.equals(cfin))
573 len++;*/
574 int idebut = je.debut.getOffset();
575 /*
576 javax.swing.text.Element pel = doc.getParagraphElement(idebut - 1);
577 javax.swing.text.Element pel2 = doc.getParagraphElement(idebut);
578 if (pel2 != pel) {
579 AttributeSet attavant = pel.getAttributes();
580 doc.setParagraphAttributes(pel2.getStartOffset(), pel2.getEndOffset() - pel2.getStartOffset(), attavant, true);
581 }
582 */ // bug avec jdk 1.4
583 doc.remove(idebut, len);
584 } catch (BadLocationException ex) {
585 System.err.println("BadLocationException: " + ex.getMessage());
586 }
587
588 supprimerEnfantDOM(je);
589 }
590
591 /**
592 * supprime l'enfant je dans le DOM
593 */
594 public void supprimerEnfantDOM(JaxeElement je) {
595 try {
596 noeud.removeChild(je.noeud);
597 } catch (DOMException ex) {
598 System.err.println("DOMException: " + ex.getMessage());
599 }
600 }
601
602 /**
603 * remplace l'enfant je à la fois dans le texte et dans le DOM
604 */
605 public void remplacerEnfant(JaxeElement je, JaxeElement newje) {
606 try {
607 doc.remove(je.debut.getOffset(), je.fin.getOffset() - je.debut.getOffset() + 1);
608 } catch (BadLocationException ex) {
609 System.err.println("BadLocationException: " + ex.getMessage());
610 }
611 newje.creer(newje.debut, newje.noeud);
612
613 remplacerEnfantDOM(je, newje);
614 }
615
616 /**
617 * remplace l'enfant je dans le DOM
618 */
619 public void remplacerEnfantDOM(JaxeElement je, JaxeElement newje) {
620 Node parent = je.noeud.getParentNode();
621 if (parent == null)
622 System.err.println("remplacerEnfantDOM: parent null !");
623 try {
624 parent.replaceChild(newje.noeud, je.noeud);
625 } catch (DOMException ex) {
626 System.err.println("DOMException: " + ex.getMessage());
627 }
628 }
629
630 /**
631 * Renvoit la profondeur dans l'arbre XML.
632 */
633 /*public int profondeur() {
634 JaxeElement p = getParent();
635 if (p == null)
636 return(0);
637 else
638 return(p.profondeur() + 1);
639 }*/
640
641 /**
642 * Indique si les descendants de l'élément doivent être indentés
643 */
644 public boolean avecIndentation() {
645 return(false);
646 }
647
648 /**
649 * Renvoit les indentations dans l'arbre XML. 0 pour la racine de l'arbre et JETableTexte.
650 */
651 public int indentations() {
652 JaxeElement p = getParent();
653 if (p != null) {
654 JaxeElement p2 = p.getParent();
655 if (p2 != null) {
656 p2 = p2.getParent();
657 if (p2 instanceof JETableTexte)
658 return(0);
659 }
660 }
661 if (p == null)
662 return(0);
663 else if (avecIndentation())
664 return(p.indentations() + 1);
665 else
666 return(p.indentations());
667 }
668
669 /**
670 * coupe la zone de texte en 2, retourne la nouvelle zone créée après celle-ci
671 */
672 public JaxeElement couper(Position pos) {
673 String t = noeud.getNodeValue();
674 String t1 = t.substring(0, pos.getOffset() - debut.getOffset());
675 String t2 = t.substring(pos.getOffset() - debut.getOffset());
676 noeud.setNodeValue(t1);
677 Node textnode2 = doc.DOMdoc.createTextNode(t2);
678 Node nextnode = noeud.getNextSibling();
679 JaxeElement parent = getParent();
680 if (nextnode == null)
681 parent.noeud.appendChild(textnode2);
682 else
683 parent.noeud.insertBefore(textnode2, nextnode);
684 JETexte je2 = new JETexte(doc);
685 je2.noeud = textnode2;
686 je2.doc = parent.doc;
687 try {
688 je2.debut = doc.createPosition(pos.getOffset());
689 je2.fin = fin;
690 fin = doc.createPosition(pos.getOffset()-1);
691 } catch (BadLocationException ex) {
692 System.err.println("BadLocationException: " + ex.getMessage());
693 }
694 doc.dom2JaxeElement.put(je2.noeud, je2);
695 return(je2);
696 }
697
698 /**
699 * fusionne cet élément avec celui donné, dans le DOM (aucun changement du texte)
700 */
701 public void fusionner(JaxeElement el) {
702 if (!(this instanceof JETexte && el instanceof JETexte))
703 return;
704 if (noeud.getNextSibling() == el.noeud) {
705 String t = el.noeud.getNodeValue();
706 noeud.setNodeValue(noeud.getNodeValue() + t);
707 fin = el.fin;
708 el.getParent().supprimerEnfantDOM(el);
709 } else if (el.noeud.getNextSibling() == noeud) {
710 String t = el.noeud.getNodeValue();
711 noeud.setNodeValue(t + noeud.getNodeValue());
712 debut = el.debut;
713 el.getParent().supprimerEnfantDOM(el);
714 }
715 }
716
717 /**
718 * regroupe les JETexte dans les enfants
719 */
720 public void regrouperTextes() {
721 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
722 while (n.getNodeType() == Node.TEXT_NODE && n.getNextSibling() != null &&
723 n.getNextSibling().getNodeType() == Node.TEXT_NODE) {
724 JaxeElement je1 = doc.getElementForNode(n);
725 JaxeElement je2 = doc.getElementForNode(n.getNextSibling());
726 je1.fusionner(je2);
727 }
728 }
729 }
730
731 public void setEffacementAutorise(boolean autorise) {
732 effacementAutorise = autorise;
733 }
734
735 public boolean getEffacementAutorise() {
736 return(effacementAutorise);
737 }
738
739 public void setEditionAutorisee(boolean autorise) {
740 editionAutorisee = autorise;
741 }
742
743 public boolean getEditionAutorisee() {
744 return(editionAutorisee);
745 }
746
747 /**
748 * Sélection de la zone de texte où se trouve cet élément
749 */
750 public void selection(boolean select) {
751 for (int i=0; i<jcomps.size(); i++) {
752 Object comp = jcomps.get(i);
753 if (comp instanceof MonBouton)
754 ((MonBouton)comp).selection(select);
755 }
756 for (Node n=noeud.getFirstChild(); n != null; n=n.getNextSibling()) {
757 JaxeElement je = doc.getElementForNode(n);
758 if (je != null)
759 je.selection(select);
760 }
761 }
762
763 /**
764 * MouseListener pour MonBouton: positionner le curseur à droite ou à gauche quand on clique sur un bord
765 */
766 public class MyMouseListener extends MouseAdapter {
767 JaxeElement jei;
768 JFrame jframe;
769 public MyMouseListener(JaxeElement obj, JFrame jframe) {
770 super();
771 jei = obj;
772 this.jframe = jframe;
773 }
774 public void mouseClicked(MouseEvent e) {
775 if (doc.textPane.isEditable() && jei.getEditionAutorisee()) {
776 jei.afficherDialogue(jframe);
777 }
778 }
779 }
780
781 /**
782 * Bouton représentant le début ou la fin d'un élément dans le texte
783 */
784 public class MonBouton extends JComponent {
785 final Color jauneLeger = new Color(255, 255, 150);
786 final Color rougeFonce = new Color(150, 0, 0);
787 final Color orange = new Color(255, 200, 150);
788 final Color bleuClair = new Color(210, 230, 255);
789 final Color violet = new Color(210, 200, 255);
790 final Color[][] ensembles = {{jauneLeger, rougeFonce, orange},
791 {bleuClair, rougeFonce, violet}};
792 JLabel label;
793 boolean valide = true;
794 boolean selectionne = false;
795 int noens = 0;
796 boolean division;
797
798 public MonBouton(String texte, boolean division) {
799 this.division = division;
800 if (division)
801 setLayout(new BorderLayout());
802 else
803 setLayout(new FlowLayout(FlowLayout.CENTER,0,0));
804 label = new JLabel(texte);
805 label.setForeground(getForeground());
806 label.setBackground(getBackground());
807 label.setOpaque(true);
808 label.setBorder(BorderFactory.createRaisedBevelBorder());
809 if (division)
810 add(label, BorderLayout.CENTER);
811 else
812 add(label);
813 setBorder(BorderFactory.createEmptyBorder(0,2,0,2));
814 label.addMouseListener(new MyMouseListener(JaxeElement.this, doc.textPane.jframe));
815 }
816 public void setText(String texte) {
817 label.setText(texte);
818 }
819 public void setValidite(boolean valide) {
820 this.valide = valide;
821 label.setBackground(getBackground());
822 }
823 public Color getBackground() {
824 if (selectionne)
825 return(ensembles[noens][1]);
826 else if (valide)
827 return(ensembles[noens][0]);
828 else
829 return(ensembles[noens][2]);
830 }
831 public Color getForeground() {
832 if (selectionne)
833 return(ensembles[noens][0]);
834 else
835 return(ensembles[noens][1]);
836 }
837 public Dimension getPreferredSize() {
838 if (division)
839 return(super.getPreferredSize());
840 Dimension d = label.getMinimumSize();
841 d.width += 4;
842 return(d);
843 }
844 public Dimension getMaximumSize() {
845 if (division)
846 return(super.getMaximumSize());
847 return(getPreferredSize());
848 }
849 public Dimension getMinimumSize() {
850 if (division)
851 return(super.getMinimumSize());
852 return(getPreferredSize());
853 }
854 public void selection(boolean select) {
855 selectionne = select;
856 label.setForeground(getForeground());
857 label.setBackground(getBackground());
858 }
859 public void setEnsembleCouleurs(int noens) {
860 this.noens = noens - (noens / ensembles.length) * ensembles.length;
861 label.setForeground(getForeground());
862 label.setBackground(getBackground());
863 }
864 }
865
866 protected String getString(String key) {
867 return(JaxeResourceBundle.getRB().getString(key));
868 }
869
870 public void changerStyle(String style, int offset, int longueur) {
871 if (style != null) {
872 Style s = doc.textPane.addStyle(null, null);
873 if (style.equals(kExposant))
874 StyleConstants.setSuperscript(s, true);
875 else if (style.equals(kIndice))
876 StyleConstants.setSubscript(s, true);
877 else if (style.equals(kItalique))
878 StyleConstants.setItalic(s, true);
879 else if (style.equals(kGras))
880 StyleConstants.setBold(s, true);
881 else if (style.equals(kSouligne))
882 StyleConstants.setUnderline(s, true);
883 else
884 s = null;
885 if (s != null)
886 doc.setCharacterAttributes(offset, longueur, s, false);
887 }
888 }
889
890 public SimpleAttributeSet attStyle(SimpleAttributeSet attorig) {
891 SimpleAttributeSet att;
892 if (attorig == null)
893 att = new SimpleAttributeSet();
894 else
895 att = attorig;
896 Element el;
897 if (noeud.getNodeType() == Node.TEXT_NODE)
898 el = (Element)getParent().noeud;
899 else
900 el = (Element)noeud;
901 Element defbalise = null;
902 if (doc.cfg != null)
903 defbalise = doc.cfg.getBaliseDef(el.getTagName());
904 if (defbalise == null)
905 return(att);
906 String style = doc.cfg.getParamFromDefinition(defbalise, "style", null);
907 if (style != null) {
908 if (style.equals(kExposant))
909 StyleConstants.setSuperscript(att, true);
910 else if (style.equals(kIndice))
911 StyleConstants.setSubscript(att, true);
912 else if (style.equals(kItalique))
913 StyleConstants.setItalic(att, true);
914 else if (style.equals(kGras))
915 StyleConstants.setBold(att, true);
916 else if (style.equals(kSouligne))
917 StyleConstants.setUnderline(att, true);
918 }
919 if (!att.isDefined(StyleConstants.FontFamily)) {
920 String police = doc.cfg.getParamFromDefinition(defbalise, "police", null);
921 if (police != null)
922 StyleConstants.setFontFamily(att, police);
923 }
924 if (!att.isDefined(StyleConstants.FontSize)) {
925 String staille = doc.cfg.getParamFromDefinition(defbalise, "taille", null);
926 if (staille != null) {
927 try {
928 int taille = Integer.parseInt(staille);
929 StyleConstants.setFontSize(att, taille);
930 } catch (NumberFormatException ex) {
931 System.err.println(ex.getClass().getName() + ": " + ex.getMessage());
932 }
933 }
934 }
935 JaxeElement jp = getParent();
936 if (jp != null)
937 return(jp.attStyle(att));
938 else
939 return(att);
940 }
941 }