Subversion Repositories eFlore/Applications.cel

Rev

Rev 1018 | 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
 
139
			public void onClick(Node node, EventObject e) {
140
				if(!arbreCharge) {
141
					expand();
142
				}
143
			}
144
 
145
			public void onExpand(Node node) {
146
				if(!arbreCharge) {
147
					obtenirArbreMotsCles();
148
					arbreCharge = true;
149
				}
150
			}
151
 
152
		});
153
 
154
		// on crée l'éditeur pour l'arbre
155
		tfEdit = new TextField();
156
		tfEdit.setAutoWidth(true);
157
		te = new TreeEditor(arbreMotsCles, tfEdit);
158
		valider = new Button("Appliquer");
159
		annuler = new Button("Annuler");
160
		arbreMotsCles.add(te);
161
 
162
		Panel panelIntermediaire = new Panel();
163
		panelIntermediaire.setLayoutData(new VerticalLayout());
164
 
165
		Panel panelBoutons = new Panel();
1118 aurelien 166
		panelBoutons.setLayout(new HorizontalLayout(130));
1018 aurelien 167
		panelBoutons.setWidth("100%");
168
 
169
		// on met en forme le layout
170
		panelIntermediaire.add(arbreMotsCles);
171
		panelBoutons.add(annuler);
172
		panelBoutons.add(valider);
173
 
174
		this.add(panelIntermediaire);
175
		this.add(panelBoutons);
1118 aurelien 176
		arbreMotsCles.setHeight("338px");
1018 aurelien 177
 
1118 aurelien 178
		Toolbar barreBouton = new Toolbar();
179
		ajouterTag = new ToolbarButton("Nouveau Tag");
180
		ajouterTag.setIcon("mot_cle_ajouter.png");
181
		renommerTag = new ToolbarButton("Renommer");
182
		renommerTag.setIcon("mot_cle_editer.png");
183
		renommerTag.disable();
184
		supprimerTag = new ToolbarButton("Supprimer");
185
		supprimerTag.setIcon("mot_cle_supprimer.png");
186
		supprimerTag.disable();
187
		barreBouton.addButton(ajouterTag);
188
		barreBouton.addSeparator();
189
		barreBouton.addButton(renommerTag);
190
		barreBouton.addSeparator();
191
		barreBouton.addButton(supprimerTag);
192
		this.setTopToolbar(barreBouton);
193
 
1018 aurelien 194
		setCloseAction(com.gwtext.client.widgets.Window.HIDE);
195
		// on ajoute les listeners
196
		ajouterListeners();
197
 
198
	}
199
 
200
 
201
	/**
202
	 * Acesseur pour l'arbre des mots clés
203
	 *
204
	 * @return le panel contenant l'arbre
205
	 */
206
	public TreePanel getArbreMotsCles() {
207
		return arbreMotsCles;
208
	}
209
 
210
	/**
211
	 * Accesseur pour l'éditeur
212
	 *
213
	 * @return l'éditeur associé à l'arbre
214
	 */
215
	public TreeEditor getTe() {
216
		return te;
217
	}
218
 
219
	/**
220
	 * Acesseur pour le TextField associé à l'éditeur
221
	 *
222
	 * @return le champ texte associé à l'éditeur
223
	 */
224
	public TextField getTfEdit() {
225
		return tfEdit;
226
	}
227
 
228
	protected abstract void surAffichageMenuContextuel(TreeNode node, EventObject e, TreeEditor te);
229
	protected abstract void surAjoutMotCle(TreeNode node, Tree arbre);
230
	protected abstract void surSuppressionMotCle(TreeNode node, Tree arbre);
231
	protected abstract void surDeplacementMotCle(TreeNode node, Tree arbre);
232
	protected abstract void surChangementTexte(TreeNode node, Tree arbre);
233
	protected abstract void surClicValider(String chaineMotsCles, Tree arbre);
234
	protected abstract void demanderArbreMotsCles(Rafraichissable r);
235
 
236
	/**
237
	 * Ajoute les listeners nécessaires pour la gestion des évènements
238
	 */
239
	private void ajouterListeners() {
240
 
241
		arbreMotsCles.addListener(new TreePanelListenerAdapter() {
242
 
243
			public void onExpandNode(TreeNode node) {
244
				cocherMotsCles(tableauMotsClesEnCours);
245
			}
246
 
247
			// gestion du clic sur un noeud
248
			public void onClick(TreeNode node, EventObject e) {
249
 
250
				e.stopEvent();
251
				gererClicNoeud(node);
1118 aurelien 252
				if(!node.equals(arbreMotsCles.getRootNode())) {
253
					supprimerTag.enable();
254
					renommerTag.enable();
255
				}
1018 aurelien 256
			}
257
 
258
			// gestion du clic droit sur un noeud
259
			public void onContextMenu(TreeNode node, EventObject e) {
260
 
261
				e.stopEvent();
262
				surAffichageMenuContextuel(node, e, getTe());
1118 aurelien 263
				if(!node.equals(arbreMotsCles.getRootNode())) {
264
					supprimerTag.enable();
265
					renommerTag.enable();
1018 aurelien 266
				}
267
			}
268
 
269
			// gestion de la modification du texte d'un noeud
270
			public void onTextChange(TreeNode node, String text, String oldText) {
271
 
1118 aurelien 272
				if(node.equals(arbreMotsCles.getRootNode())) {
273
					return;
274
				}
275
 
1018 aurelien 276
				// on récupère les informations associées au noeud
277
				TreeNode nd = node;
278
				String[] usObject = new String[2];
279
				usObject[0] = text;
280
				usObject[1] = ((String[]) nd.getUserObject())[1];
281
				nd.setUserObject(usObject);
282
 
283
				// si c'est un nouveau noeud
284
				if (ajoutNoeud) {
285
					// on considère l'ajout achevé
286
					ajoutNoeud = false;
287
					// et on notifie le médiateur de l'ajout et on lui passe
288
					// l'arbre
289
					surAjoutMotCle(nd, getArbreMotsCles().getTree());
290
				}
291
				// si c'est noeud déjà existant
292
				else {
293
					// on considère la modification achevée
294
					modifNoeud = false;
295
 
296
					if(!text.equals(oldText)) {
297
						// et on notifie le médiateur de la modification et on lui
298
						// passe l'arbre
299
						surChangementTexte(nd, getArbreMotsCles().getTree());
300
					}
301
				}
302
 
303
			}
304
 
305
			// gestion du déplacement d'un noeud
306
			public void onMoveNode(Tree tree, TreeNode node,
307
					TreeNode oldParent, TreeNode newParent, int index) {
308
				// on notifie le médiateur et on lui passe l'arbre
309
				surDeplacementMotCle(node, getArbreMotsCles().getTree());
310
			}
311
 
312
		});
313
 
314
		// gestion de la validation
315
		valider.addListener(new ButtonListenerAdapter() {
316
 
317
			// lors du clic
318
			public void onClick(Button button, EventObject e) {
319
 
320
				// on vide les mots clés en cours
321
				motsClesEnCours = "";
322
				// pour chaque noeud à partir de la racine
323
				getArbreMotsCles().getRootNode().cascade(
324
						new NodeTraversalCallback() {
325
 
326
							// on éxécute une fonction
327
							public boolean execute(Node node) {
328
 
329
								// on récupère le mot clé associé au noeud et
330
								// ses infos
331
								TreeNode tn = getArbreMotsCles().getNodeById(
332
										node.getId());
333
 
334
								String[] usObject = (String[]) tn
335
										.getUserObject();
336
								/*getIMediateur().mettreAjourMotsClesId(
337
										usObject[0], usObject[1]);*/
338
 
339
								if (tn.getUI().isChecked()) {
340
									// et les concatène à la string des mots
341
									// clés en cours
342
									motsClesEnCours += usObject[1] + ",";
343
								}
344
 
345
								return true;
346
							}
347
 
348
						});
349
 
350
				// enfin on notifie le médiateur et on lui passe l'arbre et la
351
				// liste des mots clés ainsi obtenue
352
				surClicValider(motsClesEnCours, arbreMotsCles.getTree());
353
			}
354
		});
355
 
356
		annuler.addListener(new ButtonListenerAdapter() {
357
			public void onClick(Button button, EventObject e) {
1118 aurelien 358
				hide();
1018 aurelien 359
			}
360
		});
1118 aurelien 361
 
362
		renommerTag.addListener(new ButtonListenerAdapter() {
363
			public void onClick(Button button, EventObject e) {
364
				TreeNode[] noeuds = ((MultiSelectionModel)arbreMotsCles.getSelectionModel()).getSelectedNodes();
365
				TreeNode noeudRenommage;
366
				if(noeuds.length > 0) {
367
					noeudRenommage = noeuds[noeuds.length - 1];
368
				} else {
369
					noeudRenommage = arbreMotsCles.getRootNode();
370
				}
371
				renommerNoeud(noeudRenommage);
372
			}
373
		});
374
 
375
		ajouterTag.addListener(new ButtonListenerAdapter() {
376
			@Override
377
			public void onClick(Button button, EventObject e) {
378
 
379
				TreeNode[] noeuds = ((MultiSelectionModel)arbreMotsCles.getSelectionModel()).getSelectedNodes();
380
				TreeNode noeudAjout;
381
				if(noeuds.length > 0) {
382
					noeudAjout = noeuds[noeuds.length - 1];
383
				} else {
384
					noeudAjout = arbreMotsCles.getRootNode();
385
				}
386
 
387
				ajouterNoeud(noeudAjout);
388
			}
389
		});
390
 
391
		supprimerTag.addListener(new ButtonListenerAdapter() {
392
			@Override
393
			public void onClick(Button button, EventObject e) {
394
 
395
				TreeNode[] noeuds = ((MultiSelectionModel)arbreMotsCles.getSelectionModel()).getSelectedNodes();
396
				for (int i = 0; i < noeuds.length; i++) {
397
					supprimerNoeud(noeuds[i]);
398
				}
399
			}
400
		});
1018 aurelien 401
	}
402
 
403
	/**
404
	 * Envoie une demande au médiateur pour obtenir l'arbre des mots clés
405
	 */
406
	public void obtenirArbreMotsCles() {
407
		demanderArbreMotsCles(this);
408
	}
409
 
410
	/**
411
	 * Supprime un noeud de l'arbre
412
	 *
413
	 * @param n
414
	 *            le noeud à supprimer
415
	 */
416
	public void supprimerNoeud(TreeNode n) {
417
		// si ça n'est pas la racine (qu'on ne peut pas supprimer)
418
		if (!n.getId().equals(getArbreMotsCles().getRootNode().getId())) {
1118 aurelien 419
			if(Window.confirm("Êtes vous sur de vouloir supprimer le mot clé "+n.getText()+" ?")) {
420
				// on détache le noeud et on le détruit
421
				n.getParentNode().removeChild(n);
422
				n.destroy();
423
				// puis on en notifie le médiateur en lui passant le noeud supprimé
424
				// et l'arbre
425
				surSuppressionMotCle(n, arbreMotsCles.getTree());
426
			}
1018 aurelien 427
		} else {
428
			// si l'utilisateur tente de supprimer la racine, on l'avertit de
429
			// son erreur
430
			Window.alert("Impossible de supprimer la racine de l'arbre");
431
		}
432
	}
433
 
434
	/**
435
	 * Ajoute un noeud dans l'arbre au parent donné
436
	 *
437
	 * @param parent
438
	 *            le futur parent du noeud à ajouter
439
	 */
440
	public void ajouterNoeud(TreeNode parent) {
441
 
442
		// on met l'ajout du noeud à vrai
443
		ajoutNoeud = true;
444
		// on crée un nouveau noeud vide
445
		TreeNode nd = new TreeNode("");
446
		nd.setCls("x-view-treenode-keyword");
447
		nd.setChecked(true);
448
		// on associe un objet au noeud qui contient des infos
449
		String[] usObject = new String[2];
450
		// l'objet contient le nom du noeud
451
		usObject[0] = "";
452
		usObject[1] = genererIdMotCle(nd);
453
		nd.setId(usObject[1]);
454
		nd.setUserObject(usObject);
455
		// l'identifiant d'un noeud c'est son hashcode
456
		// l'objet associé au noeud contient aussi son identifiant
457
 
458
		// on le concatène au parent et on étend ses enfants
459
		parent.appendChild(nd);
460
		parent.expand();
461
		// enfin on place le curseur et on fait apparaitre le champ d'édition
462
		// pour que l'utilisateur nomme son mot clé
463
		te.startEdit(nd);
464
 
465
	}
466
 
467
	/**
1118 aurelien 468
	 * Renomme le noeud passé en paramètre
469
	 */
470
	public void renommerNoeud(TreeNode n) {
471
		// TODO Auto-generated method stub
472
		te.startEdit(n);
473
	}
474
 
475
	/**
1018 aurelien 476
	 * Coche le noeud s'il est décoché, le décoche sinon
477
	 *
478
	 * @param node
479
	 */
480
	public void gererClicNoeud(TreeNode node) {
481
		if (node.getUI().isChecked()) {
482
			node.getUI().toggleCheck(false);
483
		} else {
484
			node.getUI().toggleCheck(true);
485
		}
486
	}
487
 
488
	/**
489
	 * Parcourt l'arbre et coche les noeud qui font partie de la liste des mots
490
	 * clés à cocher
491
	 *
492
	 * @param motsClesIds
493
	 *            un tableau contenant les identifiants des mots clés à cocher
494
	 */
495
	public void cocherMotsCles(final String[] motsClesIds) {
496
		if (getArbreMotsCles() != null
497
				&& getArbreMotsCles().getRootNode() != null) {
498
			// à partir de la racine
499
			getArbreMotsCles().getRootNode().cascade(
500
					new NodeTraversalCallback() {
501
 
502
						// pour chaque noeud
503
						public boolean execute(Node node) {
504
 
505
							getArbreMotsCles().getNodeById(node.getId())
506
							.getUI().toggleCheck(false);
507
 
508
							// on parcourt le tableau des mots clés
509
							for (int i = 0; i < motsClesIds.length; i++) {
510
								// si le mot clé fait partie des id à cocher on
511
								// le coche
512
								String usObject[] = (String[]) node
513
										.getUserObject();
514
								String nodeId = usObject[1];
515
 
516
								if (nodeId.equals(motsClesIds[i])) {
517
									getArbreMotsCles().getNodeById(nodeId)
518
											.getUI().toggleCheck(true);
519
									getArbreMotsCles().getNodeById(nodeId).ensureVisible();
520
									return true;
521
								}
522
							}
523
							// et on passe au suivant
524
							return true;
525
						}
526
 
527
					});
528
		}
529
	}
530
 
531
	/**
532
	 * Méthode héritée de l'interface rafraichissable
533
	 *
534
	 * @param nouvelleDonnees
535
	 *            les nouvelles données pour l'objet
536
	 * @param repandreRafraichissement
537
	 *            booleen qui dit si on doit répandre l'évenement
538
	 */
539
	public void rafraichir(Object nouvelleDonnees,
540
			boolean repandreRafraichissement) {
541
 
542
		if(nouvelleDonnees instanceof Tree) {
543
 
544
			Tree nouvelArbre = (Tree)nouvelleDonnees ;
545
 
546
			// on prend sa racine et on l'attache à l'arbre des mots clés
547
			Node[] rootChild = getArbreMotsCles().getRootNode().getChildNodes();
548
			for (int i = 0; i < rootChild.length; i++) {
549
 
550
				rootChild[i].remove();
551
			}
552
 
553
			copierFilsNoeud(nouvelArbre.getRootNode(),getArbreMotsCles().getRootNode());
554
 
555
			// si l'arbre n'était pas encore considéré comme instancié
556
			if (!arbreCharge) {
557
				// on signale que oui
558
				arbreCharge = true;
559
			}
560
		}
561
 
562
		// Si on reçoit un tableau de String (cas ou l'on séléectionne une
563
		// nouvelle image)
564
		if (nouvelleDonnees instanceof String[]) {
565
 
566
				// le tableau de String contient les id des mots clés associés à
567
				// l'image
568
				// on coche les mots clés contenu dans le tableau
569
				tableauMotsClesEnCours = (String[]) nouvelleDonnees;
570
				if(this.isVisible()) {
571
					cocherMotsCles(tableauMotsClesEnCours);
572
				} else {
573
					addListener(new WindowListenerAdapter() {
574
 
575
						@Override
576
						public void onShow(Component component) {
577
							cocherMotsCles(tableauMotsClesEnCours);
578
						}
579
					});
580
				}
581
		}
582
	}
583
 
584
	private String genererIdMotCle(TreeNode nd) {
585
		return "" + (nd.hashCode() + (Math.random() * 10000));
586
	}
587
 
588
	/**
589
	 * Fonction récursive qui prend deux noeuds d'arbre en paramètre et crée un
590
	 * copie du sous arbre du premier noeud, qu'elle concatène au deuxième
591
	 *
592
	 * @param ndPereOriginal
593
	 *            le père des noeuds de l'arbre original
594
	 * @param ndPereCopie
595
	 *            le père qui va recevoir les copies
596
	 */
597
	private void copierFilsNoeud(Node ndPereOriginal, TreeNode ndPereCopie) {
598
		if (ndPereCopie != null && ndPereOriginal != null) {
599
			Node[] ndNodeFils = ndPereOriginal.getChildNodes();
600
 
601
			for (int i = 0; i < ndNodeFils.length; i++) {
602
 
603
				String[] usObj = (String[]) ndNodeFils[i].getUserObject();
604
				TreeNode child = new TreeNode(usObj[0]);
605
				child.setId(usObj[1]);
606
				child.setChecked(false);
607
				child.setUserObject(usObj);
608
				ndPereCopie.appendChild(child);
609
 
610
				if (!ndNodeFils[i].isLeaf()) {
611
					copierFilsNoeud(ndNodeFils[i], child);
612
				}
613
 
614
			}
615
		}
616
	}
617
 
618
 
619
	public void activerBoutonValider(boolean activer) {
620
		valider.setVisible(activer);
621
	}
622
}
623