Subversion Repositories eFlore/Applications.cel

Rev

Rev 1118 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1018 aurelien 1
package org.tela_botanica.client.vues.image;
2
 
3
import org.tela_botanica.client.interfaces.Rafraichissable;
4
 
5
import com.google.gwt.user.client.Window;
6
import com.gwtext.client.core.EventObject;
7
import com.gwtext.client.data.Node;
8
import com.gwtext.client.data.NodeTraversalCallback;
9
import com.gwtext.client.data.Tree;
10
import com.gwtext.client.widgets.Button;
11
import com.gwtext.client.widgets.Component;
12
import com.gwtext.client.widgets.Panel;
1118 aurelien 13
import com.gwtext.client.widgets.Toolbar;
14
import com.gwtext.client.widgets.ToolbarButton;
1018 aurelien 15
import com.gwtext.client.widgets.event.ButtonListenerAdapter;
16
import com.gwtext.client.widgets.event.WindowListenerAdapter;
17
import com.gwtext.client.widgets.form.TextField;
18
import com.gwtext.client.widgets.layout.HorizontalLayout;
19
import com.gwtext.client.widgets.layout.VerticalLayout;
1118 aurelien 20
import com.gwtext.client.widgets.tree.MultiSelectionModel;
1018 aurelien 21
import com.gwtext.client.widgets.tree.TreeEditor;
22
import com.gwtext.client.widgets.tree.TreeNode;
23
import com.gwtext.client.widgets.tree.TreePanel;
24
import com.gwtext.client.widgets.tree.event.TreeNodeListenerAdapter;
25
import com.gwtext.client.widgets.tree.event.TreePanelListenerAdapter;
26
 
27
/**
28
 * Arbre des mots clés, qui est une vue rafraichissable, qui contient des mots
29
 * clés cochables et réorganisables à volonté
30
 *
31
 * @author aurelien
32
 *
33
 */
34
public abstract class FenetreGestionMotsCles extends com.gwtext.client.widgets.Window implements Rafraichissable {
35
 
36
	/**
37
	 * Le treepanel qui affiche l'arbre
38
	 */
39
	private TreePanel arbreMotsCles = null;
40
	/**
41
	 * L'éditeur qui permet de modifier les mots clés dans l'arbre
42
	 */
43
	private TreeEditor te = null;
44
	/**
45
	 * Le textfield associé à l'éditeur
46
	 */
47
	private TextField tfEdit = null;
48
	/**
49
	 * Bouton de validation
50
	 */
51
	private Button valider = null;
52
 
53
	/**
54
	 * Bouton d'annulation
55
	 */
56
	private Button annuler = null;
57
 
58
	/**
1118 aurelien 59
	 * Bouton d'ajout de tag
60
	 */
61
	private ToolbarButton ajouterTag = null;
62
 
63
	/**
64
	 * Bouton de suppression de tag
65
	 */
66
	private ToolbarButton supprimerTag = null;
67
 
68
	/**
69
	 * Bouton de renommage de tag
70
	 */
71
	private ToolbarButton renommerTag = null;
72
 
73
	/**
1018 aurelien 74
	 * Une string permettant connaitre les mots clés cochés en cours séparés par
75
	 * des virgules
76
	 */
77
	private String motsClesEnCours = "";
78
 
79
	private String[] tableauMotsClesEnCours = new String[0];
80
	/**
81
	 * Tableau contenant les mots clés qui n'ont pas encore été jaouté à l'arbre
82
	 * (sert au lazy rendering quand on reçoit des mots clés avant que le rendu
83
	 * du conteneur n'ai été effectué)
84
	 */
85
	private String[] motsClesEnAttente = new String[0];
86
	/**
87
	 * Booléen d'évènement qui sert à savoir si on est en train d'ajouter un
88
	 * noeud
89
	 */
90
	private boolean ajoutNoeud = false;
91
	/**
92
	 * Booléen d'évènement qui sert à savoir si on est en train de modifier un
93
	 * noeud
94
	 */
95
	private boolean modifNoeud = false;
96
	/**
97
	 * Booléen d'instanciation du conteneur
98
	 */
99
	private boolean arbreCharge = false;
100
	/**
101
	 * Booléen d'évènement qui sert à savoir si les mots clés ont bien été reçu
102
	 */
103
	private boolean motsCleInitialises;
104
 
105
	/**
106
	 * Constructeur avec paramètre
107
	 *
108
	 * @param im
109
	 *            le médiateur à associer
110
	 */
111
	public FenetreGestionMotsCles() {
112
 
113
		// on crée le panel
114
		setTitle("Mots clés");
115
		this.setLayoutData(new VerticalLayout());
116
 
117
		// on crée le conteneur de l'arbre
118
		arbreMotsCles = new TreePanel();
119
		// on permet le drag and drop dans l'arbre
120
		arbreMotsCles.setEnableDD(true);
121
		arbreMotsCles.setId("x-view-tree-keyword-imgs");
122
 
1118 aurelien 123
		arbreMotsCles.setSelectionModel(new MultiSelectionModel());
124
 
1018 aurelien 125
		// on crée une racine pour l'arbre
126
		TreeNode root = new TreeNode("Tags");
127
		root.setId("racine");
128
		String[] usObject = { "Mots clés", "racine" };
129
		root.setExpandable(true);
130
		arbreMotsCles.setRootNode(root);
131
		arbreMotsCles.setRootVisible(true);
132
		arbreMotsCles.setBorder(false);
133
		arbreMotsCles.setWidth(500);
134
 
135
		root.setUserObject(usObject);
136
 
137
		arbreMotsCles.getRootNode().addListener(new TreeNodeListenerAdapter() {
138
 
1292 aurelien 139
			@Override
1018 aurelien 140
			public void onClick(Node node, EventObject e) {
141
				if(!arbreCharge) {
142
					expand();
143
				}
144
			}
145
 
1292 aurelien 146
			@Override
1018 aurelien 147
			public void onExpand(Node node) {
148
				if(!arbreCharge) {
149
					obtenirArbreMotsCles();
150
					arbreCharge = true;
151
				}
152
			}
153
 
154
		});
155
 
156
		// on crée l'éditeur pour l'arbre
157
		tfEdit = new TextField();
158
		tfEdit.setAutoWidth(true);
159
		te = new TreeEditor(arbreMotsCles, tfEdit);
160
		valider = new Button("Appliquer");
161
		annuler = new Button("Annuler");
162
		arbreMotsCles.add(te);
163
 
164
		Panel panelIntermediaire = new Panel();
165
		panelIntermediaire.setLayoutData(new VerticalLayout());
166
 
167
		Panel panelBoutons = new Panel();
1118 aurelien 168
		panelBoutons.setLayout(new HorizontalLayout(130));
1018 aurelien 169
		panelBoutons.setWidth("100%");
170
 
171
		// on met en forme le layout
172
		panelIntermediaire.add(arbreMotsCles);
173
		panelBoutons.add(annuler);
174
		panelBoutons.add(valider);
175
 
176
		this.add(panelIntermediaire);
177
		this.add(panelBoutons);
1118 aurelien 178
		arbreMotsCles.setHeight("338px");
1018 aurelien 179
 
1118 aurelien 180
		Toolbar barreBouton = new Toolbar();
181
		ajouterTag = new ToolbarButton("Nouveau Tag");
182
		ajouterTag.setIcon("mot_cle_ajouter.png");
183
		renommerTag = new ToolbarButton("Renommer");
184
		renommerTag.setIcon("mot_cle_editer.png");
185
		renommerTag.disable();
186
		supprimerTag = new ToolbarButton("Supprimer");
187
		supprimerTag.setIcon("mot_cle_supprimer.png");
188
		supprimerTag.disable();
189
		barreBouton.addButton(ajouterTag);
190
		barreBouton.addSeparator();
191
		barreBouton.addButton(renommerTag);
192
		barreBouton.addSeparator();
193
		barreBouton.addButton(supprimerTag);
194
		this.setTopToolbar(barreBouton);
195
 
1018 aurelien 196
		setCloseAction(com.gwtext.client.widgets.Window.HIDE);
197
		// on ajoute les listeners
198
		ajouterListeners();
199
 
200
	}
201
 
202
 
203
	/**
204
	 * Acesseur pour l'arbre des mots clés
205
	 *
206
	 * @return le panel contenant l'arbre
207
	 */
208
	public TreePanel getArbreMotsCles() {
209
		return arbreMotsCles;
210
	}
211
 
212
	/**
213
	 * Accesseur pour l'éditeur
214
	 *
215
	 * @return l'éditeur associé à l'arbre
216
	 */
217
	public TreeEditor getTe() {
218
		return te;
219
	}
220
 
221
	/**
222
	 * Acesseur pour le TextField associé à l'éditeur
223
	 *
224
	 * @return le champ texte associé à l'éditeur
225
	 */
226
	public TextField getTfEdit() {
227
		return tfEdit;
228
	}
229
 
230
	protected abstract void surAffichageMenuContextuel(TreeNode node, EventObject e, TreeEditor te);
231
	protected abstract void surAjoutMotCle(TreeNode node, Tree arbre);
232
	protected abstract void surSuppressionMotCle(TreeNode node, Tree arbre);
233
	protected abstract void surDeplacementMotCle(TreeNode node, Tree arbre);
234
	protected abstract void surChangementTexte(TreeNode node, Tree arbre);
235
	protected abstract void surClicValider(String chaineMotsCles, Tree arbre);
236
	protected abstract void demanderArbreMotsCles(Rafraichissable r);
237
 
238
	/**
239
	 * Ajoute les listeners nécessaires pour la gestion des évènements
240
	 */
241
	private void ajouterListeners() {
242
 
243
		arbreMotsCles.addListener(new TreePanelListenerAdapter() {
244
 
1292 aurelien 245
			@Override
1018 aurelien 246
			public void onExpandNode(TreeNode node) {
247
				cocherMotsCles(tableauMotsClesEnCours);
248
			}
249
 
250
			// gestion du clic sur un noeud
1292 aurelien 251
			@Override
1018 aurelien 252
			public void onClick(TreeNode node, EventObject e) {
253
 
254
				e.stopEvent();
255
				gererClicNoeud(node);
1118 aurelien 256
				if(!node.equals(arbreMotsCles.getRootNode())) {
257
					supprimerTag.enable();
258
					renommerTag.enable();
259
				}
1018 aurelien 260
			}
261
 
262
			// gestion du clic droit sur un noeud
1292 aurelien 263
			@Override
1018 aurelien 264
			public void onContextMenu(TreeNode node, EventObject e) {
265
 
266
				e.stopEvent();
267
				surAffichageMenuContextuel(node, e, getTe());
1118 aurelien 268
				if(!node.equals(arbreMotsCles.getRootNode())) {
269
					supprimerTag.enable();
270
					renommerTag.enable();
1018 aurelien 271
				}
272
			}
273
 
274
			// gestion de la modification du texte d'un noeud
1292 aurelien 275
			@Override
1018 aurelien 276
			public void onTextChange(TreeNode node, String text, String oldText) {
277
 
1118 aurelien 278
				if(node.equals(arbreMotsCles.getRootNode())) {
279
					return;
280
				}
281
 
1018 aurelien 282
				// on récupère les informations associées au noeud
283
				TreeNode nd = node;
284
				String[] usObject = new String[2];
285
				usObject[0] = text;
286
				usObject[1] = ((String[]) nd.getUserObject())[1];
287
				nd.setUserObject(usObject);
288
 
289
				// si c'est un nouveau noeud
290
				if (ajoutNoeud) {
291
					// on considère l'ajout achevé
292
					ajoutNoeud = false;
293
					// et on notifie le médiateur de l'ajout et on lui passe
294
					// l'arbre
295
					surAjoutMotCle(nd, getArbreMotsCles().getTree());
296
				}
297
				// si c'est noeud déjà existant
298
				else {
299
					// on considère la modification achevée
300
					modifNoeud = false;
301
 
302
					if(!text.equals(oldText)) {
303
						// et on notifie le médiateur de la modification et on lui
304
						// passe l'arbre
305
						surChangementTexte(nd, getArbreMotsCles().getTree());
306
					}
307
				}
308
 
309
			}
310
 
311
			// gestion du déplacement d'un noeud
1292 aurelien 312
			@Override
1018 aurelien 313
			public void onMoveNode(Tree tree, TreeNode node,
314
					TreeNode oldParent, TreeNode newParent, int index) {
315
				// on notifie le médiateur et on lui passe l'arbre
316
				surDeplacementMotCle(node, getArbreMotsCles().getTree());
317
			}
318
 
319
		});
320
 
321
		// gestion de la validation
322
		valider.addListener(new ButtonListenerAdapter() {
323
 
324
			// lors du clic
1292 aurelien 325
			@Override
1018 aurelien 326
			public void onClick(Button button, EventObject e) {
327
 
328
				// on vide les mots clés en cours
329
				motsClesEnCours = "";
330
				// pour chaque noeud à partir de la racine
331
				getArbreMotsCles().getRootNode().cascade(
332
						new NodeTraversalCallback() {
333
 
334
							// on éxécute une fonction
1292 aurelien 335
							@Override
1018 aurelien 336
							public boolean execute(Node node) {
337
 
338
								// on récupère le mot clé associé au noeud et
339
								// ses infos
340
								TreeNode tn = getArbreMotsCles().getNodeById(
341
										node.getId());
342
 
343
								String[] usObject = (String[]) tn
344
										.getUserObject();
345
								/*getIMediateur().mettreAjourMotsClesId(
346
										usObject[0], usObject[1]);*/
347
 
348
								if (tn.getUI().isChecked()) {
349
									// et les concatène à la string des mots
350
									// clés en cours
351
									motsClesEnCours += usObject[1] + ",";
352
								}
353
 
354
								return true;
355
							}
356
 
357
						});
358
 
359
				// enfin on notifie le médiateur et on lui passe l'arbre et la
360
				// liste des mots clés ainsi obtenue
361
				surClicValider(motsClesEnCours, arbreMotsCles.getTree());
362
			}
363
		});
364
 
365
		annuler.addListener(new ButtonListenerAdapter() {
1292 aurelien 366
			@Override
1018 aurelien 367
			public void onClick(Button button, EventObject e) {
1118 aurelien 368
				hide();
1018 aurelien 369
			}
370
		});
1118 aurelien 371
 
372
		renommerTag.addListener(new ButtonListenerAdapter() {
1292 aurelien 373
			@Override
1118 aurelien 374
			public void onClick(Button button, EventObject e) {
375
				TreeNode[] noeuds = ((MultiSelectionModel)arbreMotsCles.getSelectionModel()).getSelectedNodes();
376
				TreeNode noeudRenommage;
377
				if(noeuds.length > 0) {
378
					noeudRenommage = noeuds[noeuds.length - 1];
379
				} else {
380
					noeudRenommage = arbreMotsCles.getRootNode();
381
				}
382
				renommerNoeud(noeudRenommage);
383
			}
384
		});
385
 
386
		ajouterTag.addListener(new ButtonListenerAdapter() {
387
			@Override
388
			public void onClick(Button button, EventObject e) {
389
 
390
				TreeNode[] noeuds = ((MultiSelectionModel)arbreMotsCles.getSelectionModel()).getSelectedNodes();
391
				TreeNode noeudAjout;
392
				if(noeuds.length > 0) {
393
					noeudAjout = noeuds[noeuds.length - 1];
394
				} else {
395
					noeudAjout = arbreMotsCles.getRootNode();
396
				}
397
 
398
				ajouterNoeud(noeudAjout);
399
			}
400
		});
401
 
402
		supprimerTag.addListener(new ButtonListenerAdapter() {
403
			@Override
404
			public void onClick(Button button, EventObject e) {
405
 
406
				TreeNode[] noeuds = ((MultiSelectionModel)arbreMotsCles.getSelectionModel()).getSelectedNodes();
407
				for (int i = 0; i < noeuds.length; i++) {
408
					supprimerNoeud(noeuds[i]);
409
				}
410
			}
411
		});
1018 aurelien 412
	}
413
 
414
	/**
415
	 * Envoie une demande au médiateur pour obtenir l'arbre des mots clés
416
	 */
417
	public void obtenirArbreMotsCles() {
418
		demanderArbreMotsCles(this);
419
	}
420
 
421
	/**
422
	 * Supprime un noeud de l'arbre
423
	 *
424
	 * @param n
425
	 *            le noeud à supprimer
426
	 */
427
	public void supprimerNoeud(TreeNode n) {
428
		// si ça n'est pas la racine (qu'on ne peut pas supprimer)
429
		if (!n.getId().equals(getArbreMotsCles().getRootNode().getId())) {
1118 aurelien 430
			if(Window.confirm("Êtes vous sur de vouloir supprimer le mot clé "+n.getText()+" ?")) {
431
				// on détache le noeud et on le détruit
432
				n.getParentNode().removeChild(n);
433
				n.destroy();
434
				// puis on en notifie le médiateur en lui passant le noeud supprimé
435
				// et l'arbre
436
				surSuppressionMotCle(n, arbreMotsCles.getTree());
437
			}
1018 aurelien 438
		} else {
439
			// si l'utilisateur tente de supprimer la racine, on l'avertit de
440
			// son erreur
441
			Window.alert("Impossible de supprimer la racine de l'arbre");
442
		}
443
	}
444
 
445
	/**
446
	 * Ajoute un noeud dans l'arbre au parent donné
447
	 *
448
	 * @param parent
449
	 *            le futur parent du noeud à ajouter
450
	 */
451
	public void ajouterNoeud(TreeNode parent) {
452
 
453
		// on met l'ajout du noeud à vrai
454
		ajoutNoeud = true;
455
		// on crée un nouveau noeud vide
456
		TreeNode nd = new TreeNode("");
457
		nd.setCls("x-view-treenode-keyword");
458
		nd.setChecked(true);
459
		// on associe un objet au noeud qui contient des infos
460
		String[] usObject = new String[2];
461
		// l'objet contient le nom du noeud
462
		usObject[0] = "";
463
		usObject[1] = genererIdMotCle(nd);
464
		nd.setId(usObject[1]);
465
		nd.setUserObject(usObject);
466
		// l'identifiant d'un noeud c'est son hashcode
467
		// l'objet associé au noeud contient aussi son identifiant
468
 
469
		// on le concatène au parent et on étend ses enfants
470
		parent.appendChild(nd);
471
		parent.expand();
472
		// enfin on place le curseur et on fait apparaitre le champ d'édition
473
		// pour que l'utilisateur nomme son mot clé
474
		te.startEdit(nd);
475
 
476
	}
477
 
478
	/**
1118 aurelien 479
	 * Renomme le noeud passé en paramètre
480
	 */
481
	public void renommerNoeud(TreeNode n) {
482
		// TODO Auto-generated method stub
483
		te.startEdit(n);
484
	}
485
 
486
	/**
1018 aurelien 487
	 * Coche le noeud s'il est décoché, le décoche sinon
488
	 *
489
	 * @param node
490
	 */
491
	public void gererClicNoeud(TreeNode node) {
492
		if (node.getUI().isChecked()) {
493
			node.getUI().toggleCheck(false);
494
		} else {
495
			node.getUI().toggleCheck(true);
496
		}
497
	}
498
 
499
	/**
500
	 * Parcourt l'arbre et coche les noeud qui font partie de la liste des mots
501
	 * clés à cocher
502
	 *
503
	 * @param motsClesIds
504
	 *            un tableau contenant les identifiants des mots clés à cocher
505
	 */
506
	public void cocherMotsCles(final String[] motsClesIds) {
507
		if (getArbreMotsCles() != null
508
				&& getArbreMotsCles().getRootNode() != null) {
509
			// à partir de la racine
510
			getArbreMotsCles().getRootNode().cascade(
511
					new NodeTraversalCallback() {
512
 
513
						// pour chaque noeud
1292 aurelien 514
						@Override
1018 aurelien 515
						public boolean execute(Node node) {
516
 
517
							getArbreMotsCles().getNodeById(node.getId())
518
							.getUI().toggleCheck(false);
519
 
520
							// on parcourt le tableau des mots clés
521
							for (int i = 0; i < motsClesIds.length; i++) {
522
								// si le mot clé fait partie des id à cocher on
523
								// le coche
524
								String usObject[] = (String[]) node
525
										.getUserObject();
526
								String nodeId = usObject[1];
527
 
528
								if (nodeId.equals(motsClesIds[i])) {
529
									getArbreMotsCles().getNodeById(nodeId)
530
											.getUI().toggleCheck(true);
531
									getArbreMotsCles().getNodeById(nodeId).ensureVisible();
532
									return true;
533
								}
534
							}
535
							// et on passe au suivant
536
							return true;
537
						}
538
 
539
					});
540
		}
541
	}
542
 
543
	/**
544
	 * Méthode héritée de l'interface rafraichissable
545
	 *
546
	 * @param nouvelleDonnees
547
	 *            les nouvelles données pour l'objet
548
	 * @param repandreRafraichissement
549
	 *            booleen qui dit si on doit répandre l'évenement
550
	 */
1292 aurelien 551
	@Override
1018 aurelien 552
	public void rafraichir(Object nouvelleDonnees,
553
			boolean repandreRafraichissement) {
554
 
555
		if(nouvelleDonnees instanceof Tree) {
556
 
557
			Tree nouvelArbre = (Tree)nouvelleDonnees ;
558
 
559
			// on prend sa racine et on l'attache à l'arbre des mots clés
560
			Node[] rootChild = getArbreMotsCles().getRootNode().getChildNodes();
561
			for (int i = 0; i < rootChild.length; i++) {
562
 
563
				rootChild[i].remove();
564
			}
565
 
566
			copierFilsNoeud(nouvelArbre.getRootNode(),getArbreMotsCles().getRootNode());
567
 
568
			// si l'arbre n'était pas encore considéré comme instancié
569
			if (!arbreCharge) {
570
				// on signale que oui
571
				arbreCharge = true;
572
			}
573
		}
574
 
575
		// Si on reçoit un tableau de String (cas ou l'on séléectionne une
576
		// nouvelle image)
577
		if (nouvelleDonnees instanceof String[]) {
578
 
579
				// le tableau de String contient les id des mots clés associés à
580
				// l'image
581
				// on coche les mots clés contenu dans le tableau
582
				tableauMotsClesEnCours = (String[]) nouvelleDonnees;
583
				if(this.isVisible()) {
584
					cocherMotsCles(tableauMotsClesEnCours);
585
				} else {
586
					addListener(new WindowListenerAdapter() {
587
 
588
						@Override
589
						public void onShow(Component component) {
590
							cocherMotsCles(tableauMotsClesEnCours);
591
						}
592
					});
593
				}
594
		}
595
	}
596
 
597
	private String genererIdMotCle(TreeNode nd) {
598
		return "" + (nd.hashCode() + (Math.random() * 10000));
599
	}
600
 
601
	/**
602
	 * Fonction récursive qui prend deux noeuds d'arbre en paramètre et crée un
603
	 * copie du sous arbre du premier noeud, qu'elle concatène au deuxième
604
	 *
605
	 * @param ndPereOriginal
606
	 *            le père des noeuds de l'arbre original
607
	 * @param ndPereCopie
608
	 *            le père qui va recevoir les copies
609
	 */
610
	private void copierFilsNoeud(Node ndPereOriginal, TreeNode ndPereCopie) {
611
		if (ndPereCopie != null && ndPereOriginal != null) {
612
			Node[] ndNodeFils = ndPereOriginal.getChildNodes();
613
 
614
			for (int i = 0; i < ndNodeFils.length; i++) {
615
 
616
				String[] usObj = (String[]) ndNodeFils[i].getUserObject();
617
				TreeNode child = new TreeNode(usObj[0]);
618
				child.setId(usObj[1]);
619
				child.setChecked(false);
620
				child.setUserObject(usObj);
621
				ndPereCopie.appendChild(child);
622
 
623
				if (!ndNodeFils[i].isLeaf()) {
624
					copierFilsNoeud(ndNodeFils[i], child);
625
				}
626
 
627
			}
628
		}
629
	}
630
 
631
 
632
	public void activerBoutonValider(boolean activer) {
633
		valider.setVisible(activer);
634
	}
635
}
636