Subversion Repositories eFlore/Applications.cel

Rev

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

Rev Author Line No. Line
195 david 1
package org.tela_botanica.client.vues.image;
3 aperonnet 2
 
61 jpm 3
import java.util.Iterator;
4
 
3 aperonnet 5
import org.tela_botanica.client.image.ImageMediateur;
401 aurelien 6
import org.tela_botanica.client.interfaces.ListePaginable;
3 aperonnet 7
import org.tela_botanica.client.interfaces.Rafraichissable;
989 aurelien 8
import org.tela_botanica.client.modeles.objets.ListeObservation;
9
import org.tela_botanica.client.modeles.objets.Observation;
721 aurelien 10
import org.tela_botanica.client.util.Util;
401 aurelien 11
import org.tela_botanica.client.vues.MiniBarrePaginationVue;
3 aperonnet 12
 
61 jpm 13
import com.google.gwt.user.client.Window;
99 jpm 14
import com.gwtext.client.data.Record;
3 aperonnet 15
import com.gwtext.client.data.SimpleStore;
16
import com.gwtext.client.data.Store;
17
import com.gwtext.client.dd.DragSource;
18
import com.gwtext.client.dd.DropTarget;
19
import com.gwtext.client.dd.DropTargetConfig;
20
import com.gwtext.client.widgets.Component;
155 aurelien 21
import com.gwtext.client.widgets.Container;
99 jpm 22
import com.gwtext.client.widgets.Toolbar;
3 aperonnet 23
import com.gwtext.client.widgets.event.ContainerListenerAdapter;
99 jpm 24
import com.gwtext.client.widgets.form.ComboBox;
25
import com.gwtext.client.widgets.form.event.ComboBoxListenerAdapter;
401 aurelien 26
import com.gwtext.client.widgets.grid.CellMetadata;
3 aperonnet 27
import com.gwtext.client.widgets.grid.ColumnConfig;
28
import com.gwtext.client.widgets.grid.ColumnModel;
29
import com.gwtext.client.widgets.grid.GridDragData;
30
import com.gwtext.client.widgets.grid.GridPanel;
401 aurelien 31
import com.gwtext.client.widgets.grid.Renderer;
62 jpm 32
import com.gwtext.client.widgets.grid.event.GridListenerAdapter;
401 aurelien 33
import com.gwtext.client.widgets.menu.BaseItem;
34
import com.gwtext.client.widgets.menu.Item;
35
import com.gwtext.client.widgets.menu.Menu;
36
import com.gwtext.client.widgets.menu.event.MenuListenerAdapter;
3 aperonnet 37
import com.gwtext.client.core.EventObject;
99 jpm 38
import com.gwtext.client.core.Ext;
39
import com.gwtext.client.core.ExtElement;
3 aperonnet 40
import com.gwtext.client.dd.DragData;
41
 
5 aperonnet 42
/**
63 jpm 43
 * liste d'observation pour l'association d'images aux observations
5 aperonnet 44
 * @author aurelien
45
 *
46
 */
401 aurelien 47
public class MiniListeObservationVue extends GridPanel implements Rafraichissable, ListePaginable {
3 aperonnet 48
 
5 aperonnet 49
	/**
50
	 * Le médiateur associé à la vue
51
	 */
3 aperonnet 52
	private ImageMediateur iMediateur = null ;
53
 
5 aperonnet 54
	/**
55
	 * Booléen d'instanciation
56
	 */
3 aperonnet 57
	private boolean estInstancie = false ;
58
 
5 aperonnet 59
	/**
60
	 * Le modèle de colonnes
61
	 */
62
	private ColumnModel colModel = null ;
63
 
61 jpm 64
	private SimpleStore store = null ;
65
 
408 aurelien 66
	private MiniBarrePaginationVue pgBar = new MiniBarrePaginationVue(this) ;
99 jpm 67
 
68
	private Toolbar bt = new Toolbar() ;
69
 
5 aperonnet 70
	/**
99 jpm 71
	 * Combobox permettant de selectionner le mode
72
	 * modification ou bien création
73
	 */
74
	private ComboBox selecteurMode = new ComboBox();
75
 
76
	Store storeMode = null ;
77
 
78
	private boolean liaison;
79
 
401 aurelien 80
	int pageEnCours = 0;
81
 
408 aurelien 82
	int nbElements = 0;
83
 
84
	int taillePage = 50;
85
 
638 aurelien 86
	private String modeleLieu = "IDLOCCOMMUNE, LIEUDIT, STATION";
87
 
99 jpm 88
	/**
408 aurelien 89
	 * Nombre de pages totales
90
	 */
91
	private int pageMax = 1 ;
92
 
93
	/**
5 aperonnet 94
	 * Constructeur avec arguments
95
	 * @param im le médiateur à associer à la vue
96
	 */
3 aperonnet 97
	public MiniListeObservationVue(ImageMediateur im)
98
	{
99
		iMediateur = im ;
100
 
101
		this.setId("x-view-mini-obs") ;
5 aperonnet 102
 
408 aurelien 103
		// on construit le modèle de colonnes
104
 
973 aurelien 105
		// Le store suivant est ensuite remplacé par le store contenant les données obtenus depuis le serveur (cf rafraichir)
408 aurelien 106
		Renderer colRend = new Renderer() {
107
 
1292 aurelien 108
			@Override
408 aurelien 109
			public String render(Object value, CellMetadata cellMetadata,
110
					Record record, int rowIndex, int colNum, Store store) {
111
 
112
				if(value == null || value.equals("null") || value.equals("000null") || value.equals("0000-00-00 00:00:00")) {
113
 
114
					return "" ;
115
				}
116
 
117
				return value.toString() ;
118
			}
119
 
120
		} ;
121
 
122
		Renderer dateRend = new Renderer() {
123
 
1292 aurelien 124
			@Override
408 aurelien 125
			public String render(Object value, CellMetadata cellMetadata,
126
					Record record, int rowIndex, int colNum, Store store) {
127
 
128
				if(value == null || value.equals("null") || value.equals("000null") || value.equals("0000-00-00 00:00:00")) {
129
 
130
					return "" ;
131
				}
132
				else
133
				{
134
					String dateEntiere = value.toString() ;
135
					String[] dateEtHeure = dateEntiere.split(" ", 2);
136
					if(verifierFormatDate(dateEtHeure[0])) {
137
						String[] dateFormateeTab = dateEtHeure[0].split("-",3);
138
						return dateFormateeTab[2]+"/"+dateFormateeTab[1]+"/"+dateFormateeTab[0] ;
139
					}
140
				}
141
 
142
				return value.toString() ;
143
			}
144
 
145
		} ;
146
 
5 aperonnet 147
		// on crée un store simple contenant un petit set de données et deux colonnes
973 aurelien 148
		store = new SimpleStore(new String[]{"transmis","plante","date","lieu","ordre_obs","id_obs"}, getObs());
401 aurelien 149
		ColumnConfig[] columns = {
150
		new ColumnConfig("", "transmis", 30, true, new Renderer() {
151
 
1292 aurelien 152
			@Override
401 aurelien 153
			public String render(Object value, CellMetadata cellMetadata,
154
					Record record, int rowIndex, int colNum, Store store) {
155
				if(value.equals("1"))
156
				{
157
					return "<img src=\"tela.png\"/></img>" ;
158
				}
159
				else
160
				{
161
					return "" ;
162
				}
163
			}
164
 
638 aurelien 165
		}),
408 aurelien 166
		new ColumnConfig("Taxon", "plante", 145, true, colRend),
167
		new ColumnConfig("Date", "date", 68, true, dateRend),
638 aurelien 168
		new ColumnConfig("Lieu", "lieu", 145, true, colRend),
973 aurelien 169
		new ColumnConfig("Numero", "ordre_obs", 50, true, colRend), } ;
3 aperonnet 170
 
5 aperonnet 171
        ColumnModel columnModel = new ColumnModel(columns);
172
 
173
        colModel = columnModel ;
155 aurelien 174
 
5 aperonnet 175
 
176
        setTitle("Observations");
177
        // on associe le modèle de colonnes
178
        setColumnModel(columnModel);
62 jpm 179
        setAutoScroll(true) ;
180
        setHeight("100%") ;
181
		setAutoWidth(true) ;
5 aperonnet 182
        // on autorise le drag 'n drop pour un certain groupe
3 aperonnet 183
 		this.setEnableDragDrop(true);
184
 		this.setDdGroup("DragGroupName");
5 aperonnet 185
        store.load();
186
		setStore(store) ;
187
 
401 aurelien 188
		setBottomToolbar(pgBar) ;
99 jpm 189
 
190
		Object[][] mode = {{"toutes les observations",false} , {"observations liées", true} };
191
		storeMode = new SimpleStore(new String[] { "nom_mode", "mode" },
192
				mode);
193
		storeMode.load();
194
		selecteurMode.setStore(storeMode);
195
		selecteurMode.setDisplayField("nom_mode") ;
196
		selecteurMode.setLabel("mode ") ;
197
		selecteurMode.setForceSelection(true) ;
198
		selecteurMode.setValue("toutes les observations") ;
199
		selecteurMode.setEditable(false) ;
100 jpm 200
		selecteurMode.setCls("x-selec-consult") ;
99 jpm 201
 
202
		bt = new Toolbar() ;
203
		bt.addField(selecteurMode) ;
204
 
155 aurelien 205
		//this.setAutoExpandColumn("plante");
206
 
99 jpm 207
		setTopToolbar(bt) ;
208
 
209
		selecteurMode.addListener(new ComboBoxListenerAdapter() {
210
 
1292 aurelien 211
			@Override
99 jpm 212
			public void onSelect(ComboBox comboBox, Record record, int index) {
213
 
214
				// et on met la valeur à jour dans la combobox
215
				comboBox.setValue(record.getAsString("nom_mode"));
216
				setModification(record.getAsString("mode")) ;
217
			}
218
 
219
		});
220
 
62 jpm 221
		setAutoScroll(true) ;
5 aperonnet 222
		// on configure le drag 'n drop
223
		configDragAndDrop() ;
61 jpm 224
 
62 jpm 225
		this.addGridListener(new GridListenerAdapter() {
226
 
1292 aurelien 227
			@Override
62 jpm 228
			public void onContextMenu(EventObject e) {
229
 
401 aurelien 230
				// si pas de selection, on selection au moins la ligne sur laquelle on a fait le clic
231
				if(getSelectionModel().getSelections().length <= 0) {
232
					int index = getView().findRowIndex(e);
233
			  		Record rddrop = getStore().getRecordAt(index) ;
234
					getSelectionModel().selectRecords(rddrop);
235
				}
236
 
62 jpm 237
				e.stopEvent() ;
104 jpm 238
				MenuLiaisonVue mlv = new MenuLiaisonVue(iMediateur,liaison) ;
62 jpm 239
				mlv.showAt(e.getXY()) ;
240
 
241
			}
242
 
401 aurelien 243
		}) ;
408 aurelien 244
 
245
		obtenirNombreMiniListeObservations();
3 aperonnet 246
	}
247
 
5 aperonnet 248
	/**
249
	 * Configure le drag 'n drop pour la liste
250
	 */
251
	private void configDragAndDrop()
3 aperonnet 252
	{
253
		// on choisit le texte qui sera affiché lors d'un drag 'n drop
254
		setDragDropText("Faites glisser la selection d'observations sur une image pour les lier") ;
255
 
256
		//On active le drag 'n drop
257
		this.setEnableDragDrop(true);
258
 
259
		// on fabrique la nouvelle configuration
260
		// les éléments sur lesquels on fait du drag 'n drop doivent tous avoir le même ddGroup
261
		this.setDdGroup("DragGroupName");
262
		DropTargetConfig dtc = new DropTargetConfig();
263
		dtc.setdDdGroup("DragGroupName");
264
 
265
		//La drop target permet de gérer l'évenement onDrop sur l'élement courant
266
		@SuppressWarnings("unused")
267
		DropTarget tg = new DropTarget(this, dtc)
268
		{
1292 aurelien 269
			@Override
62 jpm 270
			public boolean notifyDrop(DragSource source, EventObject e, DragData data){
3 aperonnet 271
 
272
				// si on reçoit des données provenant d'une grille
273
				if(data instanceof GridDragData)
274
		    	  {
275
					// on la convertit
276
		    		  	GridDragData gdd = (GridDragData)data ;
277
		    		  	// et on vérifie que les données ne viennent pas de l'élément courant
278
		    		  	if(gdd.getGrid().getId().equals("x-view-mini-obs"))
279
		    		  	{
280
		    		  		return false ;
281
		    		  	}
282
		    		  	else
283
		    		  	{
284
		    		  		// on appelle le médiateur
285
		    		  		return iMediateur.lierImagesDD(source, e, data) ;
286
		    		  	}
287
		    	  }
288
				return false ;
289
			}
290
 
1292 aurelien 291
			@Override
3 aperonnet 292
			public String notifyOver(DragSource source, EventObject e, DragData data){
293
			    return "x-dd-drop-ok";
294
			}
295
		};
296
 
297
	}
298
 
5 aperonnet 299
	/**
300
	 * Méthode héritée de l'interface rafraichissable
301
	 */
1292 aurelien 302
	@Override
3 aperonnet 303
	public void rafraichir(Object nouvelleDonnees,
304
			boolean repandreRaffraichissement) {
305
 
61 jpm 306
		if(nouvelleDonnees instanceof ListeObservation)
307
		{
63 jpm 308
			if(this.getView() != null)
61 jpm 309
			{
63 jpm 310
				ListeObservation data = (ListeObservation)nouvelleDonnees ;
973 aurelien 311
				String[][] listeObs = new String[data.size()][6] ;
63 jpm 312
				int i = 0 ;
61 jpm 313
 
63 jpm 314
				for (Iterator it = data.keySet().iterator(); it.hasNext();)
315
				{
316
 
1292 aurelien 317
					Observation obs=data.get(it.next());
63 jpm 318
 
638 aurelien 319
					listeObs[i][0] = obs.getTransmis();
320
					listeObs[i][1] = obs.getNomSaisi();
321
					listeObs[i][2] = obs.getDate() ;
322
					listeObs[i][3] = Util.formaterLieu(obs, modeleLieu);
323
					listeObs[i][4] = obs.getNumeroOrdre();
973 aurelien 324
					listeObs[i][5] = obs.getId();
61 jpm 325
 
63 jpm 326
					i++ ;
327
				}
328
 
973 aurelien 329
				store = new SimpleStore(new String[]{"transmis","plante","date","lieu","ordre_obs","id_obs"}, listeObs);
63 jpm 330
				store.load();
331
				this.reconfigure(store, colModel) ;
61 jpm 332
			}
63 jpm 333
			else
334
			{
335
				addListener(new ContainerListenerAdapter() {
336
 
1292 aurelien 337
					@Override
63 jpm 338
					public void onShow(Component c)
339
					{
408 aurelien 340
						obtenirNombreMiniListeObservations() ;
63 jpm 341
					}
342
 
1292 aurelien 343
					@Override
173 aurelien 344
					public void onAfterLayout(Container c)
345
					{
408 aurelien 346
						obtenirNombreMiniListeObservations() ;
173 aurelien 347
					}
348
 
63 jpm 349
				}) ;
350
			}
61 jpm 351
 
352
		}
353
 
408 aurelien 354
		// Si on reçoit un tableau d'entiers
355
		// c'est un tableau d'un seul entier qui est le nombre d'observation correspondant aux critères
356
		if(nouvelleDonnees instanceof int[])
357
		{
358
			int[] pages = (int[])nouvelleDonnees ;
359
 
360
			// on calcule le nombre de pages nécessaires et on les met à jour dans le modèle
361
			pageMax  = calculerNbPages(pages[0]) ;
362
			nbElements = pages[0];
363
 
364
			// et on notifie de le mediateur du changement des valeurs
365
			changerPageMaxEtCourante(pageMax,pageEnCours,taillePage,nbElements) ;
366
 
367
			masquerChargement();
368
			obtenirMiniListeObservations();
369
		}
370
 
371
		redimensionner();
372
 
99 jpm 373
		deMasquerChargement() ;
374
 
3 aperonnet 375
	}
376
 
61 jpm 377
	private void obtenirMiniListeObservations()
378
	{
408 aurelien 379
		iMediateur.obtenirMiniListeObservations(this, taillePage, pageEnCours) ;
61 jpm 380
	}
381
 
408 aurelien 382
	private void obtenirNombreMiniListeObservations()
383
	{
384
		iMediateur.obtenirNombreMiniListeObservations(this) ;
385
	}
386
 
5 aperonnet 387
	/**
388
	 * Renvoie le faux set de données pour le store
389
	 * @return un tableau à deux colonnes int - String
390
	 */
391
	private Object[][] getObs() {
3 aperonnet 392
	         return new Object[][]{
61 jpm 393
 
3 aperonnet 394
	         } ;
395
	 }
61 jpm 396
 
1292 aurelien 397
	@Override
62 jpm 398
	public Store getStore()
399
	{
400
		return store ;
401
	}
61 jpm 402
 
401 aurelien 403
	public MiniBarrePaginationVue getBarrePagination()
99 jpm 404
	{
405
		return pgBar ;
401 aurelien 406
	}
62 jpm 407
 
99 jpm 408
	private void setModification(String mode)
409
	{
410
		if(mode.equals("true")) {
3 aperonnet 411
 
99 jpm 412
			liaison = true ;
413
			selecteurMode.removeClass("x-selec-consult") ;
414
			selecteurMode.setCls("x-selec-liaison") ;
408 aurelien 415
			getBarrePagination().disable();
416
			doLayout();
99 jpm 417
		}
418
		else
419
		{
420
			liaison = false ;
421
			selecteurMode.removeClass("x-selec-liaison") ;
422
			selecteurMode.setCls("x-selec-consult") ;
408 aurelien 423
			getBarrePagination().enable();
424
			doLayout();
99 jpm 425
		}
426
 
427
		store.removeAll() ;
428
		iMediateur.changerModeLiaison(liaison) ;
429
 
430
	}
431
 
432
	public boolean getMode() {
433
		return liaison ;
434
	}
435
 
436
	/**
437
	 * Recherche l'élement actuellement affiché et affiche son message de chargement
438
	 */
439
	public void masquerChargement()
440
	{
441
			ExtElement masked = Ext.get(getId()) ;
442
 
443
			if (masked!=null) {
444
				masked.mask("Chargement") ;
445
			}
446
	}
447
 
448
	/**
449
	 * Recherche l'élement actuellement affiché et affiche son message de chargement
450
	 */
451
	public void deMasquerChargement()
452
	{
453
			ExtElement masked = Ext.get(getId()) ;
454
 
455
			if (masked!=null) {
456
				masked.unmask() ;
457
			}
458
	}
459
 
104 jpm 460
	public String getIdSelectionnees() {
461
 
462
		Record[] sels = getSelectionModel().getSelections() ;
463
 
408 aurelien 464
		String id = "";
465
 
466
		for(int i = 0; i < sels.length; i++) {
467
			id += ","+sels[i].getAsString("id_obs") ;
468
		}
469
 
470
		id = id.replaceFirst(",", "");
471
 
104 jpm 472
		return id ;
473
 
474
	}
99 jpm 475
 
104 jpm 476
	public void supprimerLiaison() {
477
 
478
 
479
		Record[] rdObs = getSelectionModel().getSelections() ;
480
 
481
		for(int i = 0 ; i < rdObs.length ; i++) {
482
 
483
			getStore().remove(rdObs[i]) ;
484
			this.getView().refresh() ;
485
 
486
		}
487
 
488
	}
489
 
155 aurelien 490
	public void redimensionner() {
491
		if(getView() != null) {
408 aurelien 492
 
493
			int taille = 400;
494
 
495
			if(Window.getClientHeight() > 800 ) {
496
				taille = Window.getClientHeight() - 350;
497
			}
498
			setHeight(taille);
499
			getView().setForceFit(true);
500
			doLayout();
155 aurelien 501
		}
502
		else {
408 aurelien 503
 
155 aurelien 504
		}
505
	}
401 aurelien 506
 
507
	/**
508
	 * Montre le menu de liaison aux coordonnées indiquées
509
	 * @param e
510
	 */
511
	public void montrerContextMenuLiaison(EventObject e) {
512
 
513
		final Menu liObs = new Menu();
514
		final Item lierObservation = new Item("Lier aux images selectionnées");
515
		liObs.addItem(lierObservation);
516
 
517
		liObs.addListener(new MenuListenerAdapter() {
518
 
519
			// gestion du clic sur un item
1292 aurelien 520
			@Override
401 aurelien 521
			public void onItemClick(BaseItem item, EventObject ev) {
522
 
523
				// si c'est la suppression
524
				if (item.equals(lierObservation)) {
525
					// on notifie le médiateur
526
 
527
				}
528
 
529
				// enfin, on cache le menu
530
				liObs.hide();
531
 
532
			}
533
 
534
		});
535
 
536
		liObs.showAt(e.getXY());
537
	}
538
 
1292 aurelien 539
	@Override
401 aurelien 540
	public void changerNumeroPage(int pageCourante) {
541
 
542
		pageEnCours = pageCourante ;
543
		masquerChargement();
544
 
545
		// On lance le chargerment des observations
408 aurelien 546
		iMediateur.obtenirNombreMiniListeObservations(this);
401 aurelien 547
 
408 aurelien 548
	}
549
 
550
	/**
551
	 * Appelle le modèle pour qu'il change la taille de page utilisée
552
	 * @param nouvelleTaillePage la nouvelle taille de page
553
	 */
554
 
1292 aurelien 555
	@Override
408 aurelien 556
	public void changerTaillePage(int nouvelleTaillePage)
557
	{
558
 
559
		taillePage = nouvelleTaillePage ;
560
		pageEnCours = calculerPageCourante(nbElements) ;
561
 
562
		masquerChargement();
563
 
564
		// On lance le chargerment des observations
565
		iMediateur.obtenirNombreMiniListeObservations(this);
401 aurelien 566
 
408 aurelien 567
 
568
		// et on met à jour la taille de page dans les barres d'outils
569
		pgBar.selectionnerTaillePage(nouvelleTaillePage);
570
 
571
 
401 aurelien 572
	}
408 aurelien 573
 
574
	/**
575
	 * Met à jour les barre d'outils avec des nouvelles valeurs
576
	 * @param pageMax le nombre de pages
577
	 * @param pageEncours la page en cours
578
	 * @param taillePage la taille de page
579
	 * @param nbElement le nombre d'élements par page
580
	 */
581
	public void changerPageMaxEtCourante(int pageMax, int pageEncours, int taillePage, int nbElement)
582
	{
583
 
584
		int[] pages = {pageMax,pageEncours, taillePage, nbElement} ;
585
		pgBar.rafraichir(pages, false) ;
586
 
587
	}
588
 
589
	/**
590
	 * Calcule le nombre de pages nécessaires pour afficher un nombre d'élements donnés en fonction de la taille de page
591
	 * en cours
592
	 * @param nbElements le nombre d'élements total
593
	 * @return le nombre de pages
594
	 */
595
	public int calculerNbPages(int nbElements)
596
	{
597
		// A cause de la betise de java pour les conversion implicite on fait quelques conversions manuellement
598
		// pour eviter qu'il arrondisse mal la division
599
		// nombre de pages = (nombre d'element / taille de la page) arrondie à l'entier superieur
600
 
601
		double nPage = (1.0*nbElements)/(1.0*taillePage) ;
602
		double nPageRound = Math.ceil(nPage) ;
603
		Double nPageInt = new Double(nPageRound) ;
604
 
605
		// on convertit en entier
606
		return nPageInt.intValue() ;
607
	}
401 aurelien 608
 
408 aurelien 609
 
610
	/**
611
	 * Recalcule la page en cours lors du changement du nombre d'élements
612
	 * @param nbElements le nombre d'élements total
613
	 * @return la nouvelle page encours
614
	 */
615
	public int calculerPageCourante(int nbElements)
616
	{
617
		// on calcule le nombre de page
618
		int nouvelNbPages = calculerNbPages(nbElements) ;
619
		// la nouvelle page en cours
620
		double nPageCourante = (1.0*pageEnCours)/(1.0*pageMax) * (1.0*nouvelNbPages) ;
401 aurelien 621
 
408 aurelien 622
		// on arrondit au supérieur
623
		double nPageRound = Math.ceil(nPageCourante) ;
624
		Double nPageInt = new Double(nPageRound) ;
625
 
626
		// on convertit en entier
627
		return Math.abs(nPageInt.intValue()) ;
401 aurelien 628
	}
408 aurelien 629
 
630
	public boolean verifierFormatDate(String date) {
631
 
632
		String regex = "[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}" ;
633
		if(date.matches(regex) && !date.equals("0000-00-00")) {
634
			return true ;
635
		}
636
		else {
637
			return false;
638
		}
639
	}
3 aperonnet 640
}