Statistics
| Revision:

root / trunk / libraries / libCq_CMS_praster / src / org / cresques / ui / raster / BandSetupPanel.java @ 8026

History | View | Annotate | Download (21.9 KB)

1
/*
2
 * Cresques Mapping Suite. Graphic Library for constructing mapping applications.
3
 *
4
 * Copyright (C) 2004-5.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
19
 *
20
 * For more information, contact:
21
 *
22
 * cresques@gmail.com
23
 */
24
package org.cresques.ui.raster;
25

    
26
import java.awt.Component;
27
import java.awt.Dimension;
28
import java.awt.event.ActionEvent;
29
import java.awt.event.ActionListener;
30
import java.awt.event.ComponentEvent;
31
import java.awt.event.ComponentListener;
32
import java.awt.event.KeyEvent;
33
import java.awt.event.KeyListener;
34
import java.awt.image.DataBuffer;
35
import java.io.File;
36
import java.util.Vector;
37

    
38
import javax.swing.AbstractCellEditor;
39
import javax.swing.JPanel;
40
import javax.swing.JRadioButton;
41
import javax.swing.JScrollPane;
42
import javax.swing.JTable;
43
import javax.swing.SwingConstants;
44
import javax.swing.SwingUtilities;
45
import javax.swing.event.TableModelEvent;
46
import javax.swing.event.TableModelListener;
47
import javax.swing.table.DefaultTableModel;
48
import javax.swing.table.TableCellEditor;
49
import javax.swing.table.TableCellRenderer;
50
import javax.swing.table.TableColumn;
51

    
52
import org.cresques.io.GeoRasterFile;
53

    
54

    
55
/**
56
 * Selecciona las bandas visibles en un raster. Contiene una tabla con una fila por cada
57
 * banda de la imagen. Por medio de checkbox se selecciona para cada RGB que banda de la 
58
 * imagen ser? visualizada. 
59
 * @author Luis W. Sevilla (sevilla_lui@gva.es)
60
 * @author Nacho Brodin (brodin_ign@gva.es)
61
 */
62
public class BandSetupPanel extends JPanel implements TableModelListener,
63
                                                      KeyListener,
64
                                                      ActionListener,
65
                                                      ComponentListener {
66
    final private static long serialVersionUID = -3370601314380922368L;
67

    
68
    /**
69
     * Asigna la banda del rojo
70
     */
71
    public static final int RED_BAND = GeoRasterFile.RED_BAND;
72

    
73
    /**
74
     * Asigna la banda del verde
75
     */
76
    public static final int GREEN_BAND = GeoRasterFile.GREEN_BAND;
77

    
78
    /**
79
     * Asigna banda del azul
80
     */
81
    public static final int BLUE_BAND = GeoRasterFile.BLUE_BAND;
82
    private final static String[] columnNames = { "R", "G", "B", "Band" };
83

    
84
    /**
85
     * Nombre del panel
86
     */
87
    private String nom = "Bandas";
88
    private FileList fileList = null;
89
    private JTable rgbTable = null;
90
    private JScrollPane rgbBandAssignPane = null;
91
    RGBBandAsignTableModel tableModel = null;
92
    private int sizeX = 445;
93
    private int sizeY = 174;
94
    private byte mode = 3;
95
    private int[] col = { 0, 1 }; //Ultima y penultima columnas activadas del jtable para cuando hay 2 bandas seleccionadas en el combo
96

    
97
    /**
98
     * This method initializes
99
     *
100
     */
101
    public BandSetupPanel() {
102
        super();
103
        initialize();
104
    }
105

    
106
    /**
107
     * This method initializes this
108
     *
109
     * @return void
110
     */
111
    void initialize() {
112
        this.setPreferredSize(new Dimension(sizeX, sizeY));
113
        this.setLayout(null);
114
        this.setLocation(0, 0);
115
        this.setSize(445, 239);
116
        this.add(getFileList(), null);
117
        this.add(getRGBBandAssignPane(), null);
118
        getFileList().getJComboBox().addKeyListener(this);
119
        getFileList().getJComboBox().addActionListener(this);
120
        this.addComponentListener(this);
121
    }
122

    
123
    /**
124
     * Obtiene el nombre del panel
125
     * @return Cadena con el nombre del panel
126
     */
127
    public String getName(){
128
            return this.nom;
129
    }
130
    
131
    /**
132
     * A?ade la lista de georasterfiles a la tabla
133
     * @param files
134
     */
135
    public void addFiles(GeoRasterFile[] files) {
136
        for (int i = 0; i < files.length; i++) {
137
            String fName = files[i].getName();
138
            getFileList().addFName(fName);
139

    
140
            //((DefaultListModel)fileList.getJList().getModel()).addElement(fName);
141
            String bandName = new File(fName).getName();
142
            String bandType = "";
143

    
144
            switch (files[i].getDataType()) {
145
            case DataBuffer.TYPE_BYTE:
146
                bandType = "8U";
147
                break;
148
            case DataBuffer.TYPE_INT:
149
                bandType = "32";
150
                break;
151
            case DataBuffer.TYPE_DOUBLE:
152
                bandType = "64";
153
                break;
154
            case DataBuffer.TYPE_FLOAT:
155
                bandType = "32";
156
                break;
157
            case DataBuffer.TYPE_SHORT:
158
                bandType = "16";
159
                break;
160
            case DataBuffer.TYPE_USHORT:
161
                bandType = "16U";
162
                break;
163
            case DataBuffer.TYPE_UNDEFINED:
164
                bandType = "??";
165
                break;
166
            }
167

    
168
            if (files[i].getBandCount() > 1) {
169
                for (int b = 0; b < files[i].getBandCount(); b++)
170
                    addBand((b + 1) + " [" + bandType + "] " + bandName);
171
            } else {
172
                addBand("1 [" + bandType + "] " + bandName);
173
            }
174
        }
175
    }
176

    
177
    /**
178
     * Elimina un fichero de la lista
179
     * @param file Nombre del fichero a eliminar
180
     */
181
    public void removeFile(String file) {
182
        getFileList().removeFName(file);
183

    
184
        for (int i = 0;
185
                 i < ((DefaultTableModel) rgbTable.getModel()).getRowCount();
186
                 i++) {
187
            //Si el fichero borrado estaba seleccionado como banda visible. Pasaremos
188
            //esta visibilidad a la banda inmediata superior y si esta acci?n produce 
189
            //una excepci?n (porque no hay) se pasa al inmediato inferior.
190
            if (((String) ((DefaultTableModel) rgbTable.getModel()).getValueAt(i, 3)).endsWith(file)) {
191
                try {
192
                    if (((Boolean) ((DefaultTableModel) rgbTable.getModel()).getValueAt(i, 0)).booleanValue()) {
193
                        ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), i - 1, 0);
194
                    }
195
                } catch (ArrayIndexOutOfBoundsException exc) {
196
                    ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), i + 1, 0);
197
                }
198

    
199
                try {
200
                    if (((Boolean) ((DefaultTableModel) rgbTable.getModel()).getValueAt(i, 1)).booleanValue()) {
201
                        ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), i - 1, 1);
202
                    }
203
                } catch (ArrayIndexOutOfBoundsException exc) {
204
                    ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), i + 1, 1);
205
                }
206

    
207
                try {
208
                    if (((Boolean) ((DefaultTableModel) rgbTable.getModel()).getValueAt(i, 2)).booleanValue()) {
209
                        ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), i - 1, 2);
210
                    }
211
                } catch (ArrayIndexOutOfBoundsException exc) {
212
                    ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), i + 1, 2);
213
                }
214

    
215
                ((DefaultTableModel) rgbTable.getModel()).removeRow(i);
216
                i--; //Ojo! que hemos eliminado una fila
217
            }
218
        }
219
    }
220

    
221
    /**
222
    * Cuando cambiamos el combo de seleccion de numero de bandas a visualizar
223
    * debemos resetear la tabla de checkbox para que no haya activados m?s de
224
    * los permitidos
225
    * @param mode
226
    */
227
    private void resetMode(int mode) {
228
        //Reseteamos los checkbox
229
        for (int i = 0; i < (rgbTable.getColumnCount() - 1); i++)
230
            for (int j = 0; j < rgbTable.getRowCount(); j++)
231
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(false), j, i);
232

    
233
        //Asignamos los valores
234
        if (this.getNBands() >= 3) {
235
            switch (mode) {
236
            case 3:
237
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 2, 2);
238
            case 2:
239
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 1, 1);
240
            case 1:
241
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 0, 0);
242
            }
243
        } else if (this.getNBands() == 2) {
244
            switch (mode) {
245
            case 3:
246
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 1, 2);
247
            case 2:
248
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 1, 1);
249
            case 1:
250
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 0, 0);
251
            }
252
        } else if (this.getNBands() == 1) {
253
            switch (mode) {
254
            case 3:
255
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 0, 2);
256
            case 2:
257
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 0, 1);
258
            case 1:
259
                ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(true), 0, 0);
260
            }
261
        }
262

    
263
        col[0] = 0;
264
        col[1] = 1;
265
    }
266

    
267
    /**
268
     * Asigna modo 1, 2, o 3 bandas. El modo 1 solo permite seleccionar en la
269
     * tabla un checkbox, el 2 dos checkbox en distintar bandas y el 3 tres checkbox
270
     * tambi?n en distintas bandas.
271
     * @param mode
272
     */
273
    private void setMode(int mode) {
274
        //Solo hay un checkbox activado
275
        if (mode == 1) {
276
            for (int i = 0; i < rgbTable.getRowCount(); i++)
277
                for (int j = 0; j < (rgbTable.getColumnCount() - 1); j++) {
278
                    if ((i != rgbTable.getSelectedRow()) ||
279
                            (j != rgbTable.getSelectedColumn())) {
280
                        ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(false), i, j);
281
                    }
282
                }
283

    
284
            //Hay dos checkbox activados
285
        } else if (mode == 2) {
286
            int n = 0;
287

    
288
            for (int i = 0; i < (rgbTable.getColumnCount() - 1); i++)
289
                for (int j = 0; j < rgbTable.getRowCount(); j++)
290
                    if (((Boolean) ((DefaultTableModel) rgbTable.getModel()).getValueAt(j, i)).booleanValue()) {
291
                        n++;
292
                    }
293

    
294
            //Si se ha seleccionado 3 bandas hay eliminar una de ellas. Siempre ser? la m?s antigua que se clickeo
295
            if (n > 2) {
296
                for (int i = 0; i < rgbTable.getRowCount(); i++)
297
                    ((DefaultTableModel) rgbTable.getModel()).setValueAt(new Boolean(false), i, col[0]);
298
            }
299

    
300
            //Rotamos el punto pinchado m?s antiguo para que se eliminen alternativamente
301
            if ((col[0] == rgbTable.getSelectedColumn()) ||
302
                    ((col[0] != rgbTable.getSelectedColumn()) &&
303
                    (col[1] != rgbTable.getSelectedColumn()))) {
304
                col[0] = col[1];
305
                col[1] = rgbTable.getSelectedColumn();
306
            }
307

    
308
            //El modo 3 es el comportamiento original
309
        } else if (mode == 3) {
310
            return;
311
        }
312
    }
313

    
314
    /**
315
     * Obtiene el panel que contiene la lista de ficheros por banda.
316
     * @return Panel FileList
317
     */
318
    public FileList getFileList() {
319
        if (fileList == null) {
320
            fileList = new FileList();
321
            fileList.setBounds(9, 9, 428, 110);
322
        }
323

    
324
        return fileList;
325
    }
326

    
327
    /**
328
     * This method initializes jTable
329
     *
330
     * @return javax.swing.JTable
331
     */
332
    private JScrollPane getRGBBandAssignPane() {
333
        if (rgbBandAssignPane == null) {
334
            rgbBandAssignPane = new JScrollPane(getRGBTable());
335
            rgbBandAssignPane.setBounds(10, 123, 427, 104);
336

    
337
            TableColumn column = null;
338

    
339
            for (int i = 0; i < 3; i++) {
340
                column = rgbTable.getColumnModel().getColumn(i);
341
                column.setCellRenderer(new RadioColumnRenderer());
342
                column.setCellEditor(new RadioColumnEditor());
343
                column.setMaxWidth(22);
344
                column.setMinWidth(22);
345
            }
346

    
347
            //                        frame.setContentPane(scrollPane);
348
        }
349

    
350
        return rgbBandAssignPane;
351
    }
352

    
353
    /**
354
     * Obtiene la Tabla
355
     * @return Tabla de bandas de la imagen
356
     */
357
    public JTable getRGBTable() {
358
        if (rgbTable == null) {
359
            tableModel = new RGBBandAsignTableModel();
360
            tableModel.addTableModelListener(this);
361
            rgbTable = new JTable(tableModel);
362
            rgbTable.setPreferredScrollableViewportSize(new Dimension(328, 72));
363
        }
364

    
365
        return rgbTable;
366
    }
367

    
368
    /**
369
     * Asigna al combo de n?mero de bandas a mostrar el valor correspondiente
370
     * dependiendo del n?mero de bandas de la imagen.
371
     */
372
    public void setList() {
373
        String[] l = null;
374

    
375
        if (this.getNBands() == 1) {
376
            l = new String[1];
377
            l[0] = "1";
378
        }
379

    
380
        if (this.getNBands() == 2) {
381
            l = new String[2];
382
            l[0] = "1";
383
            l[1] = "2";
384
        }
385

    
386
        if (this.getNBands() == 3) {
387
            l = new String[3];
388
            l[0] = "1";
389
            l[1] = "2";
390
            l[2] = "3";
391
        }
392

    
393
        if (this.getNBands() > 0) {
394
            getFileList().setList(l);
395
        }
396
    }
397

    
398
    /**
399
     * A?ade una banda a la tabla  bandas de la imagen asignandole un
400
     * nombre y valor a los checkbox
401
     * @param bandName        Nombre de la banda
402
     */
403
    private void addBand(String bandName) {
404
        Vector v = new Vector();
405
        v.add(new Boolean(false));
406
        v.add(new Boolean(false));
407
        v.add(new Boolean(false));
408
        v.add(bandName);
409
        ((DefaultTableModel) rgbTable.getModel()).addRow(v);
410
    }
411

    
412
    /**
413
     * Obtiene el n?mero de bandas de la lista
414
     * @return
415
     */
416
    public int getNBands() {
417
        return ((DefaultTableModel) rgbTable.getModel()).getRowCount();
418
    }
419

    
420
    /**
421
     * Obtiene el nombre de la banda de la posici?n i de la tabla
422
     * @param i
423
     * @return
424
     */
425
    public String getBandName(int i) {
426
        String s = (String) ((DefaultTableModel) rgbTable.getModel()).getValueAt(i, 3);
427
        return s.substring(s.lastIndexOf("[8U]") + 5, s.length());
428
    }
429

    
430
    /**
431
     * Mantiene la asignaci?n entre R, G o B y la banda de la imagen que le corresponde 
432
     * @param nBand        Banda de la imagen que corresponde
433
     * @param flag        R, G o B se selecciona por medio de un flag que los identifica
434
     */
435
    public void assignBand(int nBand, int flag) {
436
        Boolean t = new Boolean(true);
437

    
438
        if ((flag & RED_BAND) == RED_BAND)
439
            ((DefaultTableModel) rgbTable.getModel()).setValueAt(t, nBand, 0);
440
        
441
        if ((flag & GREEN_BAND) == GREEN_BAND) 
442
            ((DefaultTableModel) rgbTable.getModel()).setValueAt(t, nBand, 1);
443
        
444
        if ((flag & BLUE_BAND) == BLUE_BAND)
445
            ((DefaultTableModel) rgbTable.getModel()).setValueAt(t, nBand, 2);
446
    }
447

    
448
    /**
449
     * Obtiene la correspondencia entre el R, G o B y la banda asignada
450
     * @param flag        R, G o B se selecciona por medio de un flag que los identifica
451
     * @return        Banda de la imagen asignada al flag pasado por par?metro
452
     */
453
    public int getAssignedBand(int flag) {
454
        if ((flag & RED_BAND) == RED_BAND) {
455
            for (int nBand = 0; nBand < rgbTable.getRowCount(); nBand++)
456
                if (((Boolean) ((DefaultTableModel) rgbTable.getModel()).getValueAt(nBand,
457
                                                                                        0)).booleanValue()) {
458
                    return nBand;
459
                }
460
        }
461

    
462
        if ((flag & GREEN_BAND) == GREEN_BAND) {
463
            for (int nBand = 0; nBand < rgbTable.getRowCount(); nBand++)
464
                if (((Boolean) ((DefaultTableModel) rgbTable.getModel()).getValueAt(nBand,
465
                                                                                        1)).booleanValue()) {
466
                    return nBand;
467
                }
468
        }
469

    
470
        if ((flag & BLUE_BAND) == BLUE_BAND) {
471
            for (int nBand = 0; nBand < rgbTable.getRowCount(); nBand++)
472
                if (((Boolean) ((DefaultTableModel) rgbTable.getModel()).getValueAt(nBand,
473
                                                                                        2)).booleanValue()) {
474
                    return nBand;
475
                }
476
        }
477

    
478
        return -1;
479
    }
480

    
481
    /* (non-Javadoc)
482
     * @see javax.swing.event.TableModelListener#tableChanged(javax.swing.event.TableModelEvent)
483
     */
484
    public void tableChanged(TableModelEvent e) {
485
        rgbTable.revalidate();
486
        rgbBandAssignPane.revalidate();
487
        revalidate();
488
    }
489

    
490
    /* (non-Javadoc)
491
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
492
     */
493
    public void keyPressed(KeyEvent arg0) {
494
        // TODO Auto-generated method stub
495
    }
496

    
497
    /* (non-Javadoc)
498
     * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
499
     */
500
    public void keyReleased(KeyEvent arg0) {
501
        // TODO Auto-generated method stub
502
    }
503

    
504
    /* (non-Javadoc)
505
     * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
506
     */
507
    public void keyTyped(KeyEvent arg0) {
508
        // TODO Auto-generated method stub
509
    }
510

    
511
    /**
512
     *Evento que activado cuando se modifica el n?mero de bandas que se desean visualizar.
513
     *Se obtiene este n?mero y se llama a la funci?n que controla el cambio de n?mero
514
     *de bandas visualizadas. 
515
     */
516
    public void actionPerformed(ActionEvent arg0) {
517
        String vBands = (String) getFileList().getJComboBox().getSelectedItem();
518

    
519
        if (vBands != null) {
520
            if (vBands.compareTo("3") == 0) {
521
                mode = 3;
522
            }
523

    
524
            if (vBands.compareTo("2") == 0) {
525
                mode = 2;
526
            }
527

    
528
            if (vBands.compareTo("1") == 0) {
529
                mode = 1;
530
            }
531

    
532
            resetMode(mode);
533
        }
534
    }
535

    
536
    class RadioColumnEditor extends AbstractCellEditor
537
        implements TableCellEditor {
538
        final private static long serialVersionUID = -3370601314380922368L;
539
        public JRadioButton theRadioButton;
540

    
541
        public RadioColumnEditor() {
542
            super();
543
            theRadioButton = new JRadioButton();
544
            theRadioButton.addActionListener(new ActionListener() {
545
                    public void actionPerformed(ActionEvent event) {
546
                        fireEditingStopped() ;
547
                        setMode(mode);
548
                    }
549
                });
550
        }
551

    
552
        public Component getTableCellEditorComponent(JTable table, Object obj,
553
                                                     boolean isSelected,
554
                                                     int row, int col) {
555
            theRadioButton.setHorizontalAlignment(SwingUtilities.CENTER);
556

    
557
            Boolean lValueAsBoolean = (Boolean) obj;
558
            theRadioButton.setSelected(lValueAsBoolean.booleanValue());
559

    
560
            return theRadioButton;
561
        }
562

    
563
        public Object getCellEditorValue() {
564
            return new Boolean(theRadioButton.isSelected());
565
        }
566
    }
567

    
568
    class RadioColumnRenderer extends JRadioButton implements TableCellRenderer {
569
        final private static long serialVersionUID = -3370601314380922368L;
570

    
571
        public Component getTableCellRendererComponent(JTable table,
572
                                                       Object value,
573
                                                       boolean isSelected,
574
                                                       boolean hasFocus,
575
                                                       int row, int column) {
576
            if (value == null) {
577
                this.setSelected(false);
578
            }
579

    
580
            Boolean ValueAsBoolean = (Boolean) value;
581
            this.setSelected(ValueAsBoolean.booleanValue());
582
            this.setHorizontalAlignment(SwingConstants.CENTER);
583

    
584
            return this;
585
        }
586
    }
587

    
588
    class RGBBandAsignTableModel extends DefaultTableModel {
589
        final private static long serialVersionUID = -3370601314380922368L;
590

    
591
        public RGBBandAsignTableModel() {
592
            super(new Object[0][4], columnNames);
593
        }
594

    
595
        public Class getColumnClass(int c) {
596
            if (c < 3) {
597
                return Boolean.class;
598
            }
599

    
600
            return String.class;
601
        }
602

    
603
        public void setValueAt(Object value, int row, int col) {
604
            if ((col < 3) && ((Boolean) value).booleanValue()) {
605
                for (int i = 0; i < getRowCount(); i++) {
606
                    if (i != row) {
607
                        setValueAt(new Boolean(false), i, col);
608
                    }
609
                }
610
            }
611

    
612
            super.setValueAt(value, row, col);
613
        }
614

    
615
        public void addNew() {
616
            super.addRow(new Object[] {
617
                             new Boolean(false), new Boolean(false),
618
                             new Boolean(false), ""
619
                         });
620
        }
621
        
622
              
623
    }
624

    
625
        public void componentHidden(ComponentEvent e) {
626
                // TODO Auto-generated method stub
627
                
628
        }
629

    
630
        public void componentMoved(ComponentEvent e) {
631
                // TODO Auto-generated method stub
632
                
633
        }
634
        /**
635
         * Redimensiona el panel cuando se redimensiona el contenedor de ?ste
636
         */
637
        public void componentResized(ComponentEvent e) {
638
                if(e.getSource() == this){
639
                        int nWidth = this.getSize().width;
640
                        int nHeight = this.getSize().height;
641
                        int difWidth = nWidth - 445;
642
                        int difHeight = nHeight - 239;
643
                        this.fileList.setResize(difWidth, difHeight);
644
                        this.rgbBandAssignPane.setBounds(10, 123 + difHeight/2, 427 + difWidth, 104 + difHeight/2);
645
                        
646
                }
647
                
648
        }
649

    
650
        public void componentShown(ComponentEvent e) {
651
                // TODO Auto-generated method stub
652
                
653
        }
654
} //  @jve:decl-index=0:visual-constraint="10,10"