View Javadoc
1   package fr.ifremer.tutti.ui.swing.content.operation.catches.species.edit;
2   
3   /*
4    * #%L
5    * Tutti :: UI
6    * %%
7    * Copyright (C) 2012 - 2014 Ifremer
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the 
12   * License, or (at your option) any later version.
13   * 
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   * 
19   * You should have received a copy of the GNU General Public 
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  import com.google.common.base.Joiner;
26  import com.google.common.collect.Lists;
27  import com.google.common.collect.Sets;
28  import fr.ifremer.tutti.persistence.entities.data.SpeciesBatch;
29  import fr.ifremer.tutti.persistence.entities.data.SpeciesBatchFrequency;
30  import fr.ifremer.tutti.persistence.entities.referential.Species;
31  import fr.ifremer.tutti.service.TuttiDecorator;
32  import jaxx.runtime.SwingUtil;
33  import org.apache.commons.collections4.CollectionUtils;
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.jdesktop.swingx.JXTable;
37  import org.jdesktop.swingx.table.TableColumnExt;
38  import org.nuiton.decorator.DecoratorUtil;
39  import org.nuiton.jaxx.application.swing.table.AbstractApplicationTableModel;
40  
41  import javax.swing.ButtonGroup;
42  import javax.swing.JLabel;
43  import javax.swing.JPopupMenu;
44  import javax.swing.JRadioButtonMenuItem;
45  import javax.swing.JSeparator;
46  import javax.swing.SwingUtilities;
47  import javax.swing.table.JTableHeader;
48  import java.awt.Point;
49  import java.awt.event.ActionEvent;
50  import java.awt.event.ActionListener;
51  import java.awt.event.MouseAdapter;
52  import java.awt.event.MouseEvent;
53  import java.io.Serializable;
54  import java.util.Collection;
55  import java.util.Collections;
56  import java.util.List;
57  import java.util.Set;
58  
59  import static org.nuiton.i18n.I18n.t;
60  
61  /**
62   * Helper methods aroun sorting species rows.
63   * Created on 1/8/14.
64   *
65   * @author Tony Chemit - chemit@codelutin.com
66   * @since 3.0
67   */
68  public class SpeciesBatchRowHelper {
69  
70      /** Logger. */
71      private static final Log log = LogFactory.getLog(SpeciesBatchRowHelper.class);
72  
73      public static final String SPECIES_DECORATOR = "decorator";
74  
75      public static final String SPECIES_DECORATOR_INDEX = "decoratorIndex";
76  
77      public static SpeciesBatchDecoratorComparator<SpeciesBatchRowModel> getSpeciesRowComparator(JXTable table) {
78          TableColumnExt speciesColumn = table.getColumnExt(SpeciesBatchTableModel.SPECIES);
79  
80          SpeciesBatchDecoratorComparator<SpeciesBatchRowModel> comparator =
81                  (SpeciesBatchDecoratorComparator<SpeciesBatchRowModel>) speciesColumn.getComparator();
82  
83          SpeciesBatchDecorator<SpeciesBatchRowModel> decorator = getSpeciesColumnDecorator(table);
84  
85          boolean comparatorNull = comparator == null;
86          if (comparatorNull) {
87  
88              // first time coming here, add the comparator
89              comparator = (SpeciesBatchDecoratorComparator) decorator.getCurrentComparator();
90          }
91  
92          if (comparatorNull) {
93  
94              // affect it to colum
95              speciesColumn.setComparator(comparator);
96          }
97          return comparator;
98      }
99  
100     protected static SpeciesBatchDecorator<SpeciesBatchRowModel> getSpeciesColumnDecorator(JXTable table) {
101         TableColumnExt speciesColumn = table.getColumnExt(SpeciesBatchTableModel.SPECIES);
102         return (SpeciesBatchDecorator<SpeciesBatchRowModel>) SpeciesBatchRowHelper.getSpeciesColumnDecorator(speciesColumn);
103     }
104 
105     public static <E extends SpeciesBatch> int getIndexToInsert(List<E> rows,
106                                                                 E newRow,
107                                                                 SpeciesSortMode sortMode,
108                                                                 SpeciesBatchDecorator decorator) {
109 
110         int result;
111 
112         int rowsSize = rows.size();
113         if (sortMode == SpeciesSortMode.NONE) {
114             // after the last row
115             result = rowsSize;
116         } else {
117 
118             // get the universe of species
119             Set<Species> speciesSet = Sets.newHashSet();
120             for (E row : rows) {
121                 speciesSet.add(row.getSpecies());
122             }
123             Species newSpecies = newRow.getSpecies();
124 
125             boolean speciestoAdd = speciesSet.add(newSpecies);
126 
127             int nbSpecies = speciesSet.size();
128 
129             // sort it
130             List<Species> speciesList = Lists.newArrayList(speciesSet);
131             TuttiDecorator.TuttiDecoratorComparator<Species> comparator = decorator.getOriginalComparator();
132             comparator.init(decorator, speciesList);
133             Collections.sort(speciesList, comparator);
134             if (sortMode == SpeciesSortMode.DESC) {
135 
136                 // reserve order
137                 Collections.reverse(speciesList);
138             }
139 
140             int indexOf = speciesList.indexOf(newSpecies);
141 
142             if (!speciestoAdd) {
143                 // add after all rows of the species
144                 indexOf++;
145             }
146 
147             if (indexOf == 0) {
148 
149                 // limit case: first row
150                 result = 0;
151             } else if (indexOf >= nbSpecies) {
152 
153                 // limit case: last row
154                 result = rowsSize;
155             } else {
156 
157                 // must insert before this species
158                 Species beforeSpecies;
159                 if (speciestoAdd) {
160                     beforeSpecies = speciesList.get(indexOf + 1);
161                 } else {
162                     beforeSpecies = speciesList.get(indexOf);
163                 }
164                 result = 0;
165                 for (E row : rows) {
166                     Species species = row.getSpecies();
167                     if (beforeSpecies.equals(species)) {
168                         break;
169                     }
170                     result++;
171                 }
172             }
173         }
174         return result;
175     }
176 
177     public static TuttiDecorator<Species> getSpeciesColumnDecorator(TableColumnExt tableColumn) {
178         return (TuttiDecorator<Species>) tableColumn.getClientProperty(SPECIES_DECORATOR);
179     }
180 
181     public static <R extends Serializable, T extends AbstractApplicationTableModel<R>> void installSpeciesColumnComparatorPopup(JXTable table,
182                                                                                                                                 TableColumnExt speciesColumn,
183                                                                                                                                 SpeciesSortableRowModel optionalModel,
184                                                                                                                                 String... tips) {
185 
186         ButtonGroup buttonGroup = new ButtonGroup();
187 
188         SpeciesDecoratorListener<R, T> speciesDecoratorListener =
189                 new SpeciesDecoratorListener<>(table, buttonGroup, speciesColumn, optionalModel);
190 
191         TuttiDecorator<Species> decorator =
192                 getSpeciesColumnDecorator(speciesColumn);
193 
194         JPopupMenu popup = new JPopupMenu();
195         popup.add(new JLabel(t("tutti.ui.change.species.decorator")));
196         popup.add(new JSeparator());
197 
198         for (int i = 0, nbContext = decorator.getNbContext(); i < nbContext; i++) {
199             String property = decorator.getProperty(i);
200 
201             String i18nName = "tutti.property." + property;
202             speciesColumn.putClientProperty(i18nName, tips[i]);
203             JRadioButtonMenuItem item = new JRadioButtonMenuItem(tips[i]);
204             item.putClientProperty(SPECIES_DECORATOR_INDEX, i);
205             item.addActionListener(speciesDecoratorListener);
206             if (i == 0) {
207                 // select the first property (as it is the
208                 item.setSelected(true);
209             }
210             buttonGroup.add(item);
211             popup.add(item);
212         }
213 
214         // recompute the header tip using the decorator
215         speciesDecoratorListener.recomputeSpeciesColumnTip();
216 
217         // listen when to show popup menu on cell header
218         table.getTableHeader().addMouseListener(
219                 new ShowSpeciesDecoratorPopupListener(popup));
220 
221     }
222 
223     public static <R extends SpeciesBatch> void sortSpeciesRows(JXTable table,
224                                                                 SpeciesBatchDecorator decorator,
225                                                                 List<R> rows,
226                                                                 SpeciesSortMode speciesSortMode) {
227 
228         SpeciesBatchDecoratorComparator comparator = getSpeciesRowComparator(table);
229         comparator.setSpeciesSortMode(speciesSortMode);
230         comparator.init(decorator, rows);
231 
232         switch (speciesSortMode) {
233 
234             case NONE:
235                 // nothing special to do
236                 break;
237             case ASC:
238             case DESC:
239                 // sort
240                 DecoratorUtil.sort(decorator, rows, decorator.getContextIndex());
241                 break;
242         }
243     }
244 
245     /**
246      * Calcule le poids total des mensurations.
247      *
248      * S'il n'y a pas de meunsration retourne {@code null}, idem si l'une des mensurations n'a pas de poids.
249      *
250      * @return la somme des poids des mensurations
251      * @since 3.10
252      */
253     public static <F extends SpeciesBatchFrequency> Float getFrequenciesTotalWeight(Collection<F> frequency) {
254 
255         if (CollectionUtils.isEmpty(frequency)) {
256 
257             // pas de mensuration, donc poids total {@code null}
258             return null;
259 
260         }
261 
262         float frequencyTotalWeight = 0;
263         for (SpeciesBatchFrequency aFrequency : frequency) {
264 
265             if (aFrequency.getWeight() == null) {
266 
267                 // on a trouve une mensuration sans poids, on ne peut donc pas calcule
268                 // de poids total
269                 // on retourne {@code null}
270                 return null;
271             }
272 
273             frequencyTotalWeight += aFrequency.getWeight();
274 
275         }
276 
277         return frequencyTotalWeight;
278 
279     }
280 
281     protected static class SpeciesDecoratorListener<R extends Serializable, T extends AbstractApplicationTableModel<R>> implements ActionListener {
282 
283         protected final JXTable table;
284 
285         protected final ButtonGroup buttonGroup;
286 
287         protected final SpeciesSortableRowModel optionalModel;
288 
289         protected final TuttiDecorator<Species> decorator;
290 
291         protected final TableColumnExt column;
292 
293         public SpeciesDecoratorListener(JXTable table,
294                                         ButtonGroup buttonGroup,
295                                         TableColumnExt speciesColumn,
296                                         SpeciesSortableRowModel optionalModel) {
297             this.table = table;
298             this.buttonGroup = buttonGroup;
299             this.optionalModel = optionalModel;
300             this.column = speciesColumn;
301             this.decorator = getSpeciesColumnDecorator(column);
302         }
303 
304         @Override
305         public void actionPerformed(ActionEvent e) {
306 
307             JRadioButtonMenuItem source = (JRadioButtonMenuItem) e.getSource();
308             buttonGroup.setSelected(source.getModel(), true);
309 
310             Integer index =
311                     (Integer) source.getClientProperty(SPECIES_DECORATOR_INDEX);
312 
313             if (log.isInfoEnabled()) {
314                 log.info("Selected decorator context index: " + index);
315             }
316 
317             // do the decoration from here
318             decorator.setContextIndex(index);
319 
320             column.setComparator(decorator.getCurrentComparator());
321 
322             // recompute the header tip
323             recomputeSpeciesColumnTip();
324 
325             if (optionalModel != null) {
326 
327                 // set new index in model
328                 optionalModel.setSpeciesDecoratorContextIndex(index);
329             } else {
330 
331                 // reload table data
332                 T tableModel = (T) table.getModel();
333 
334                 // keep selected rows
335                 List<R> rowsToReSelect = Lists.newArrayList();
336                 for (int rowIndex : SwingUtil.getSelectedModelRows(table)) {
337                     R row = tableModel.getEntry(rowIndex);
338                     rowsToReSelect.add(row);
339                 }
340 
341                 // fire model (will reload the comparator)
342                 tableModel.fireTableDataChanged();
343 
344                 // reselect rows
345                 for (R row : rowsToReSelect) {
346                     int modelRowIndex = tableModel.getRowIndex(row);
347                     SwingUtil.addRowSelectionInterval(table, modelRowIndex);
348                 }
349             }
350         }
351 
352         public void recomputeSpeciesColumnTip() {
353             List<String> tips = Lists.newArrayList();
354             for (int i = 0, nbContext = decorator.getNbContext(); i < nbContext; i++) {
355                 String property = decorator.getProperty(i);
356 
357                 String i18nName = "tutti.property." + property;
358                 String tip = (String) column.getClientProperty(i18nName);
359                 tips.add(tip);
360             }
361             String tip = Joiner.on(" - ").join(tips);
362             column.setToolTipText(tip);
363         }
364     }
365 
366     protected static class ShowSpeciesDecoratorPopupListener extends MouseAdapter {
367 
368         private final JPopupMenu popup;
369 
370         public ShowSpeciesDecoratorPopupListener(JPopupMenu popup) {
371             this.popup = popup;
372         }
373 
374         @Override
375         public void mouseClicked(MouseEvent e) {
376             JTableHeader source = (JTableHeader) e.getSource();
377             Point point = e.getPoint();
378             int columnIndex = source.columnAtPoint(point);
379 
380             boolean rightClick = SwingUtilities.isRightMouseButton(e);
381             if (columnIndex == 0 && rightClick) {
382                 e.consume();
383                 popup.show(source, e.getX(), e.getY());
384             }
385         }
386     }
387 }