View Javadoc
1   package fr.ifremer.tutti.ui.swing.content.operation;
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.Preconditions;
26  import com.google.common.collect.Lists;
27  import fr.ifremer.tutti.persistence.entities.TuttiEntities;
28  import fr.ifremer.tutti.persistence.entities.data.Cruise;
29  import fr.ifremer.tutti.persistence.entities.data.Program;
30  import fr.ifremer.tutti.persistence.entities.protocol.OperationFieldMappingRow;
31  import fr.ifremer.tutti.persistence.entities.referential.Caracteristic;
32  import fr.ifremer.tutti.persistence.entities.referential.Gear;
33  import fr.ifremer.tutti.persistence.entities.referential.Person;
34  import fr.ifremer.tutti.persistence.entities.referential.TuttiLocation;
35  import fr.ifremer.tutti.persistence.entities.referential.Vessel;
36  import fr.ifremer.tutti.service.DecoratorService;
37  import fr.ifremer.tutti.service.PersistenceService;
38  import fr.ifremer.tutti.type.CoordinateEditorType;
39  import fr.ifremer.tutti.ui.swing.TuttiUIContext;
40  import fr.ifremer.tutti.ui.swing.content.operation.fishing.AbstractCaracteristicTabUIModel;
41  import fr.ifremer.tutti.ui.swing.content.operation.fishing.GearUseFeatureTabUIModel;
42  import fr.ifremer.tutti.ui.swing.content.operation.fishing.VesselUseFeatureTabUIModel;
43  import fr.ifremer.tutti.ui.swing.content.operation.fishing.actions.CancelEditFishingOperationAction;
44  import fr.ifremer.tutti.ui.swing.util.AbstractTuttiTabContainerUIHandler;
45  import fr.ifremer.tutti.ui.swing.util.TuttiBeanMonitor;
46  import fr.ifremer.tutti.ui.swing.util.caracteristics.GearCaracteristicListCellRenderer;
47  import fr.ifremer.tutti.util.DateTimes;
48  import fr.ifremer.tutti.util.Distances;
49  import jaxx.runtime.validator.swing.SwingValidator;
50  import org.apache.commons.collections4.CollectionUtils;
51  import org.apache.commons.lang3.BooleanUtils;
52  import org.apache.commons.lang3.StringUtils;
53  import org.apache.commons.lang3.time.DateUtils;
54  import org.apache.commons.logging.Log;
55  import org.apache.commons.logging.LogFactory;
56  import org.jdesktop.swingx.JXDatePicker;
57  import org.nuiton.jaxx.application.bean.JavaBeanObjectUtil;
58  import org.nuiton.jaxx.application.swing.tab.TabHandler;
59  
60  import javax.swing.JButton;
61  import javax.swing.JComboBox;
62  import javax.swing.JComponent;
63  import javax.swing.JOptionPane;
64  import javax.swing.JTabbedPane;
65  import java.awt.BorderLayout;
66  import java.awt.Color;
67  import java.awt.Component;
68  import java.awt.event.ActionEvent;
69  import java.awt.event.ItemEvent;
70  import java.awt.event.ItemListener;
71  import java.beans.PropertyChangeEvent;
72  import java.beans.PropertyChangeListener;
73  import java.util.Calendar;
74  import java.util.Collection;
75  import java.util.Date;
76  import java.util.List;
77  import java.util.Set;
78  
79  import static org.nuiton.i18n.I18n.t;
80  
81  /**
82   * Handler for UI {@link EditFishingOperationUI}.
83   *
84   * @author Tony Chemit - chemit@codelutin.com
85   * @since 0.1
86   */
87  public class EditFishingOperationUIHandler extends AbstractTuttiTabContainerUIHandler<EditFishingOperationUIModel, EditFishingOperationUI>
88          implements TabHandler {
89  
90      /** Logger. */
91      private static final Log log =
92              LogFactory.getLog(EditFishingOperationUIHandler.class);
93  
94      /**
95       * listener to set the same end date as the start date
96       * when the user changes it only (remove this listener before selecting the
97       * fishing operation)
98       */
99      private final PropertyChangeListener startDateListener = new PropertyChangeListener() {
100 
101         public void propertyChange(PropertyChangeEvent evt) {
102             //check if the user changed the date and not only the time)
103             Date oldDate = (Date) evt.getOldValue();
104             Date newDate = (Date) evt.getNewValue();
105             if (newDate != null && getModel().getGearShootingEndDate() == null &&
106                     (oldDate == null || !DateUtils.isSameDay(oldDate, newDate))) {
107                 getModel().setGearShootingEndDate(newDate);
108             }
109         }
110     };
111 
112     private final PropertyChangeListener coordinatePropertiesListener = new PropertyChangeListener() {
113 
114         private List<String> properties = Lists.newArrayList(
115                 EditFishingOperationUIModel.PROPERTY_FISHING_OPERATION_RECTILIGNE,
116                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE,
117                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS,
118                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS_SIGN,
119                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS_DEGREE,
120                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS_MINUTE,
121                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS_SECOND,
122                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD,
123                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD_SIGN,
124                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD_DEGREE,
125                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD_MINUTE,
126                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD_DECIMAL,
127                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE,
128                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS,
129                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS_SIGN,
130                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS_DEGREE,
131                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS_MINUTE,
132                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS_SECOND,
133                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD,
134                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD_SIGN,
135                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD_DEGREE,
136                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD_MINUTE,
137                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD_DECIMAL,
138                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE,
139                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS,
140                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS_SIGN,
141                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS_DEGREE,
142                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS_MINUTE,
143                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS_SECOND,
144                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD,
145                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD_SIGN,
146                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD_DEGREE,
147                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD_MINUTE,
148                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD_DECIMAL,
149                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE,
150                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS,
151                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS_SIGN,
152                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS_DEGREE,
153                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS_MINUTE,
154                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS_SECOND,
155                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD,
156                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD_SIGN,
157                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD_DEGREE,
158                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD_MINUTE,
159                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD_DECIMAL
160         );
161 
162         @Override
163         public void propertyChange(PropertyChangeEvent evt) {
164             if (properties.contains(evt.getPropertyName())) {
165                 EditFishingOperationUIModel source = (EditFishingOperationUIModel) evt.getSource();
166                 if (source.isFishingOperationRectiligne()) {
167                     source.computeDistance();
168                 }
169 
170                 if (log.isInfoEnabled()) {
171                     log.info(String.format("Property %s changed to %s", evt.getPropertyName(), evt.getNewValue()));
172                 }
173             }
174         }
175     };
176 
177     public void setQuadrantValue(int value) {
178         if (log.isInfoEnabled()) {
179             log.info("New quadrant value: " + value);
180         }
181     }
182 
183     public boolean isQuadrantSelected(Integer value, int requiredValue) {
184         return value != null && value == requiredValue;
185     }
186 
187     /**
188      * To monitor changes on the incoming fishing operation.
189      *
190      * @since 0.3
191      */
192     private TuttiBeanMonitor<EditFishingOperationUIModel> fishingOperationMonitor;
193 
194     //------------------------------------------------------------------------//
195     //-- AbstractTuttiUIHandler methods                                     --//
196     //------------------------------------------------------------------------//
197 
198     @Override
199     public void beforeInit(EditFishingOperationUI ui) {
200         super.beforeInit(ui);
201 
202         this.fishingOperationMonitor = new TuttiBeanMonitor<>(
203                 EditFishingOperationUIModel.PROPERTY_STATION_NUMBER,
204                 EditFishingOperationUIModel.PROPERTY_FISHING_OPERATION_NUMBER,
205                 EditFishingOperationUIModel.PROPERTY_STRATA,
206                 EditFishingOperationUIModel.PROPERTY_SUB_STRATA,
207 
208                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE,
209                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS,
210                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS_SIGN,
211                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS_DEGREE,
212                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS_MINUTE,
213                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMS_SECOND,
214                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD,
215                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD_SIGN,
216                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD_DEGREE,
217                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LATITUDE_DMD_MINUTE,
218                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE,
219                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS,
220                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS_SIGN,
221                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS_DEGREE,
222                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS_MINUTE,
223                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMS_SECOND,
224                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD,
225                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD_SIGN,
226                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD_DEGREE,
227                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_LONGITUDE_DMD_MINUTE,
228                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE,
229                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS,
230                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS_SIGN,
231                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS_DEGREE,
232                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS_MINUTE,
233                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMS_SECOND,
234                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD,
235                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD_SIGN,
236                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD_DEGREE,
237                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LATITUDE_DMD_MINUTE,
238                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE,
239                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS,
240                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS_SIGN,
241                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS_DEGREE,
242                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS_MINUTE,
243                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMS_SECOND,
244                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD,
245                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD_SIGN,
246                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD_DEGREE,
247                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_LONGITUDE_DMD_MINUTE,
248 
249                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_DATE,
250                 EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_END_DATE,
251                 EditFishingOperationUIModel.PROPERTY_FISHING_OPERATION_RECTILIGNE,
252                 EditFishingOperationUIModel.PROPERTY_TRAWL_DISTANCE,
253                 EditFishingOperationUIModel.PROPERTY_FISHING_OPERATION_VALID,
254                 EditFishingOperationUIModel.PROPERTY_LOCATION,
255                 EditFishingOperationUIModel.PROPERTY_RECORDER_PERSON,
256                 EditFishingOperationUIModel.PROPERTY_SECONDARY_VESSEL,
257                 EditFishingOperationUIModel.PROPERTY_VESSEL,
258                 EditFishingOperationUIModel.PROPERTY_GEAR,
259                 EditFishingOperationUIModel.PROPERTY_MULTIRIG_AGGREGATION,
260                 EditFishingOperationUIModel.PROPERTY_COMMENT);
261 
262         EditFishingOperationUIModel model = new EditFishingOperationUIModel();
263         model.setCoordinateEditorType(getConfig().getCoordinateEditorType());
264         model.setValidationContext(getContext().getValidationContext());
265 
266         model.addPropertyChangeListener(EditFishingOperationUIModel.PROPERTY_STRATA, evt -> {
267             EditFishingOperationUIModel source = (EditFishingOperationUIModel) evt.getSource();
268             TuttiLocation newStrata = (TuttiLocation) evt.getNewValue();
269             onSelectedStrata(source, newStrata);
270         });
271 
272         model.addPropertyChangeListener(EditFishingOperationUIModel.PROPERTY_SUB_STRATA, evt -> {
273             EditFishingOperationUIModel source = (EditFishingOperationUIModel) evt.getSource();
274             TuttiLocation newStrata = (TuttiLocation) evt.getNewValue();
275             onSelectedSubStrata(source, newStrata);
276         });
277 
278         getContext().addPropertyChangeListener(TuttiUIContext.PROPERTY_VALIDATION_CONTEXT, evt -> getModel().setValidationContext((String) evt.getNewValue()));
279 
280         model.addPropertyChangeListener(EditFishingOperationUIModel.PROPERTY_FISHING_OPERATION_VALID, evt -> {
281             Boolean valid = (Boolean) evt.getNewValue();
282             Color color = null;
283             Color fontColor = Color.BLACK;
284             if (valid == Boolean.TRUE) {
285                 color = Color.GREEN;
286 
287             } else if (valid == Boolean.FALSE) {
288                 color = Color.RED;
289                 fontColor = Color.WHITE;
290             }
291             Component tab = getTabPanel().getTabComponentAt(0);
292             tab.setForeground(fontColor);
293             tab.setBackground(color);
294         });
295 
296         // enable or not the import from column file
297         if (getDataContext().isProtocolFilled()) {
298             Collection<OperationFieldMappingRow> operationFieldMapping = getDataContext().getProtocol().getOperationFieldMapping();
299             for (OperationFieldMappingRow row : operationFieldMapping) {
300                 if ((EditFishingOperationUIModel.PROPERTY_STATION_NUMBER.equals(row.getField())
301                         || EditFishingOperationUIModel.PROPERTY_FISHING_OPERATION_NUMBER.equals(row.getField())
302                         || EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_DATE.equals(row.getField()))
303                         && StringUtils.isNotBlank(row.getImportColumn())) {
304 
305                     model.setImportFromColumnFileEnabled(true);
306                     break;
307                 }
308             }
309         }
310 
311         ui.setContextValue(model);
312 
313         fishingOperationMonitor.setBean(model);
314     }
315 
316     @Override
317     public void afterInit(EditFishingOperationUI ui) {
318 
319         ui.getSecondaryVesselList().getBeforeFilterPanel().add(ui.getSecondaryVesselFilterPane(), BorderLayout.CENTER);
320 
321         ui.getFishingOperationValidPanel().remove(
322                 ui.getFishingOperationResetRadio());
323 
324         final EditFishingOperationUIModel model = getModel();
325 
326         initUI(ui);
327 
328         List<Person> people = getDataContext().getPersons();
329         initBeanList(ui.getRecorderPersonList(),
330                      people,
331                      model.getRecorderPerson());
332 
333         Cruise cruise = getDataContext().getCruise();
334         Preconditions.checkNotNull(cruise,
335                                    "Could not find cruise in ui context");
336         ui.getMultirigAggregationField().setEnabled(cruise.getMultirigNumber() != 1);
337 
338         initBeanFilterableComboBox(ui.getGearComboBox(),
339                                    Lists.<Gear>newArrayList(),
340                                    null,
341                                    DecoratorService.GEAR_WITH_RANK_ORDER);
342 
343 
344         JComboBox combobox = ui.getGearComboBox().getCombobox();
345         combobox.setRenderer(new GearCaracteristicListCellRenderer(
346                 combobox.getRenderer(),
347                 getDecorator(Caracteristic.class, DecoratorService.CARACTERISTIC_WITH_UNIT),
348                 getDecorator(Gear.class, DecoratorService.GEAR_WITH_RANK_ORDER),
349                 true));
350         combobox.addItemListener(new ItemListener() {
351 
352 //            Map<Gear, String> tips = Maps.newHashMap();
353 
354             public void itemStateChanged(ItemEvent e) {
355                 Object item = e.getItem();
356                 if (e.getStateChange() == ItemEvent.SELECTED) {
357                     if (log.isDebugEnabled()) {
358                         log.debug("itemStateChanged selected " + item + " - " + (item != null ? item.getClass() : null));
359                     }
360 
361                     JComboBox combo = (JComboBox) e.getSource();
362                     JComponent jC = (JComponent) combo.getEditor().getEditorComponent();
363 
364                     GearCaracteristicListCellRenderer renderer = (GearCaracteristicListCellRenderer) combo.getRenderer();
365 
366                     String toolTipText = renderer.getToolTipText((Gear) item);
367 
368                     jC.setToolTipText(toolTipText);
369                 } else {
370                     JComboBox combo = (JComboBox) e.getSource();
371                     JComponent jC = (JComponent) combo.getEditor().getEditorComponent();
372 
373                     jC.setToolTipText(null);
374                 }
375             }
376         });
377 
378         Program program = getDataContext().getProgram();
379         Preconditions.checkNotNull(program,
380                                    "Could not find program in ui context");
381 
382         TuttiLocation zone = program.getZone();
383 
384         TuttiLocation strata = model.getStrata();
385 
386         String strataId = strata == null ? null : strata.getId();
387 
388         TuttiLocation subStrata = model.getSubStrata();
389         String substrataId = subStrata == null ? null : subStrata.getId();
390 
391         TuttiLocation location = model.getLocation();
392 
393         PersistenceService persistenceService = getPersistenceService();
394         String zoneId = zone.getId();
395         List<TuttiLocation> stratas =
396                 Lists.newArrayList(persistenceService.getAllFishingOperationStrata(zoneId));
397 
398         List<TuttiLocation> subStratas =
399                 Lists.newArrayList(persistenceService.getAllFishingOperationSubStrata(zoneId, strataId));
400 
401         List<TuttiLocation> locations =
402                 Lists.newArrayList(persistenceService.getAllFishingOperationLocation(zoneId, strataId, substrataId));
403 
404         initBeanFilterableComboBox(ui.getStrataComboBox(), stratas, strata);
405 
406         initBeanFilterableComboBox(ui.getSubStrataComboBox(), subStratas, subStrata);
407 
408         initBeanFilterableComboBox(ui.getLocationComboBox(), locations, location);
409 
410         initBeanList(ui.getSecondaryVesselList(),
411                      Lists.<Vessel>newArrayList(),
412                      model.getSecondaryVessel());
413 
414         // reset TrawlDistance when fishingOperationRectiligne pass to false
415         // see https://forge.codelutin.com/issues/5807
416         model.addPropertyChangeListener(EditFishingOperationUIModel.PROPERTY_FISHING_OPERATION_RECTILIGNE,
417                                         evt -> {
418 
419                                             EditFishingOperationUIModel source = (EditFishingOperationUIModel) evt.getSource();
420                                             Boolean newValue = (Boolean) evt.getNewValue();
421                                             if (BooleanUtils.isFalse(newValue)) {
422                                                 source.setTrawlDistance(null);
423                                             }
424                                         });
425 
426         // Change vessel list and gear list when vessel type changes
427         model.addPropertyChangeListener(EditFishingOperationUIModel.PROPERTY_SECONDARY_VESSEL_TYPE, evt -> {
428 
429             EditFishingOperationUIModel model1 =
430                     (EditFishingOperationUIModel) evt.getSource();
431 
432             boolean modify = model1.isModify();
433 
434             SecondaryVesselTypeEnum vesselType =
435                     (SecondaryVesselTypeEnum) evt.getNewValue();
436 
437             List<Vessel> vessels = Lists.newArrayList();
438 
439             if (vesselType != null) {
440                 switch (vesselType) {
441 
442                     case SCIENTIFIC:
443 
444                         vessels = model1.getScientificSecondaryVessel();
445                         break;
446                     case FISHING:
447 
448                         vessels = model1.getFishingSecondaryVessel();
449                         break;
450                     case ONLY_CRUISE:
451 
452                         vessels = model1.getOnlyCruisSecondaryVessel();
453                         break;
454                     case ALL:
455                     default:
456 
457                         vessels = model1.getAllSecondaryVessel();
458                 }
459             }
460 
461             // clean vessel filter
462             EditFishingOperationUIHandler.this.ui.getSecondaryVesselList().getHandler().clearFilters();
463 
464             // get selected vessel
465             List<Vessel> selected = Lists.newArrayList(EditFishingOperationUIHandler.this.ui.getSecondaryVesselList().getModel().getSelected());
466 
467             // set new universe of vessel
468             EditFishingOperationUIHandler.this.ui.getSecondaryVesselList().getModel().setUniverse(vessels);
469 
470             // push back selected vessel
471             EditFishingOperationUIHandler.this.ui.getSecondaryVesselList().getModel().setSelected(selected);
472 
473             // push back modify state
474             model1.setModify(modify);
475         });
476 
477         model.setSecondaryVesselType(null);
478 
479         changeValidatorContext(model.getValidationContext(), ui.getValidator());
480         listenValidationTableHasNoFatalError(ui.getValidator(), model);
481 
482         setCustomTab(0, model);
483         listModelIsModify(model);
484 
485         //init gear use features
486         GearUseFeatureTabUIModel gearUseFeatureModel =
487                 ui.getGearUseFeatureTabContent().getModel();
488         gearUseFeatureModel.setAvailableCaracteristics(getDataContext().getCaracteristics());
489         setCustomTab(1, gearUseFeatureModel);
490         listenModelModifiy(gearUseFeatureModel);
491 
492         //init vessel use features
493 
494         VesselUseFeatureTabUIModel vesselUseFeatureModel =
495                 ui.getVesselUseFeatureTabContent().getModel();
496         vesselUseFeatureModel.setAvailableCaracteristics(getDataContext().getCaracteristics());
497         listenModelModifiy(vesselUseFeatureModel);
498         setCustomTab(2, vesselUseFeatureModel);
499 
500         // listen when id becomes empty or not to update cancel action and button
501         getModel().addPropertyChangeListener(
502                 EditFishingOperationUIModel.PROPERTY_ID,
503                 evt -> {
504                     JButton button = EditFishingOperationUIHandler.this.ui.getCancelButton();
505                     CancelEditFishingOperationAction action =
506                             getContext().getActionFactory().getLogicAction(button);
507                     if (evt.getNewValue() == null) {
508                         button.setText(t("tutti.editFishingOperation.action.cancelEditFishingOperation"));
509                         button.setToolTipText(t("tutti.editFishingOperation.action.cancelEditFishingOperation.tip"));
510                         button.setMnemonic(t("tutti.editFishingOperation.action.cancelEditFishingOperation.mnemonic").charAt(0));
511                         action.setActionDescription(t("tutti.editFishingOperation.action.cancelEditFishingOperation.tip"));
512 
513                     } else {
514                         button.setText(t("tutti.editFishingOperation.action.resetEditFishingOperation"));
515                         button.setToolTipText(t("tutti.editFishingOperation.action.resetEditFishingOperation.tip"));
516                         button.setMnemonic(t("tutti.editFishingOperation.action.resetEditFishingOperation.mnemonic").charAt(0));
517                         action.setActionDescription(t("tutti.editFishingOperation.action.resetEditFishingOperation.tip"));
518                     }
519                 });
520     }
521 
522     @Override
523     public JComponent getComponentToFocus() {
524         return getUI().getStationNumberField();
525     }
526 
527     @Override
528     protected Set<String> getPropertiesToIgnore() {
529         Set<String> result = super.getPropertiesToIgnore();
530         result.add(EditFishingOperationUIModel.PROPERTY_ATTACHMENT);
531         result.add(EditFishingOperationUIModel.PROPERTY_SECONDARY_VESSEL_TYPE);
532         result.add(EditFishingOperationUIModel.PROPERTY_SECONDARY_VESSEL_TYPE_ALL);
533         result.add(EditFishingOperationUIModel.PROPERTY_SECONDARY_VESSEL_TYPE_SCIENTIFIC);
534         result.add(EditFishingOperationUIModel.PROPERTY_SECONDARY_VESSEL_TYPE_FISHING);
535         result.add(EditFishingOperationUIModel.PROPERTY_SECONDARY_VESSEL_TYPE_ONLY_CRUISE);
536         return result;
537     }
538 
539     @Override
540     public void onCloseUI() {
541 
542         if (log.isDebugEnabled()) {
543             log.debug("closing: " + ui);
544         }
545 
546         clearValidators();
547 
548         ui.getFishingOperationAttachmentsButton().onCloseUI();
549         closeUI(ui.getGearUseFeatureTabContent());
550         closeUI(ui.getVesselUseFeatureTabContent());
551     }
552 
553     @Override
554     public SwingValidator<EditFishingOperationUIModel> getValidator() {
555         return ui.getValidator();
556     }
557 
558     @Override
559     public void setDate(ActionEvent event, String property) {
560         JXDatePicker field = (JXDatePicker) event.getSource();
561         Date value = field.getDate();
562         Date date = (Date) JavaBeanObjectUtil.getProperty(getModel(), property);
563         if (value != null && date != null) {
564             Calendar cal = DateUtils.toCalendar(date);
565             value = DateUtils.setHours(value, cal.get(Calendar.HOUR_OF_DAY));
566             value = DateUtils.setMinutes(value, cal.get(Calendar.MINUTE));
567         }
568         JavaBeanObjectUtil.setProperty(getModel(), property, value);
569     }
570 
571     //------------------------------------------------------------------------//
572     //-- AbstractTuttiTabContainerUIHandler methods                         --//
573     //------------------------------------------------------------------------//
574 
575     @Override
576     public boolean onTabChanged(int currentIndex, int newIndex) {
577         ui.getFishingOperationAttachmentsButton().onCloseUI();
578         return super.onTabChanged(currentIndex, newIndex);
579     }
580 
581     @Override
582     public JTabbedPane getTabPanel() {
583         return ui.getFishingOperationTabPane();
584     }
585 
586     @Override
587     public boolean removeTab(int i) {
588         return false;
589     }
590 
591     //------------------------------------------------------------------------//
592     //-- TabHandler methods                                                 --//
593     //------------------------------------------------------------------------//
594 
595     @Override
596     public boolean onHideTab(int currentIndex, int newIndex) {
597         boolean result;
598         ui.getFishingOperationAttachmentsButton().onCloseUI();
599         if (isAModelModified()) {
600 
601             // something was modified
602 
603             EditFishingOperationUIModel model = getModel();
604 
605             if (model.isValid()) {
606 
607                 // ask if user want to save, do not save or cancel action
608 
609                 String message;
610 
611                 if (TuttiEntities.isNew(model.getFishingOperation())) {
612                     message = t("tutti.editFishingOperation.askSaveBeforeLeaving.createFishingOperation");
613                 } else {
614                     message = t("tutti.editFishingOperation.askSaveBeforeLeaving.saveFishingOperation");
615                 }
616 
617                 int answer = askSaveBeforeLeaving(message);
618 //                ActionEvent event = new ActionEvent(this, newIndex, null);
619                 switch (answer) {
620                     case JOptionPane.OK_OPTION:
621                         getContext().getActionEngine().runAction(ui.getSaveButton());
622                         result = true;
623                         break;
624 
625                     case JOptionPane.NO_OPTION:
626                         getContext().getActionEngine().runAction(ui.getCancelButton());
627                         result = true;
628                         break;
629                     default:
630 
631                         // other case, use cancel action
632                         result = false;
633                 }
634             } else {
635 
636                 // model is not valid, ask user to continue or not
637 
638                 result = askCancelEditBeforeLeaving(
639                         t("tutti.editFishingOperation.askCancelEditBeforeLeaving.cancelEditFishingOperation"));
640 
641                 if (result) {
642                     // ok will revert any modification
643                     getContext().getActionEngine().runAction(ui.getCancelButton());
644                 }
645             }
646 
647         } else {
648 
649             // model not modify, can change tab
650             result = true;
651         }
652         return result;
653     }
654 
655     @Override
656     public void onShowTab(int currentIndex, int newIndex) {
657         // validation if no operation
658         // see http://forge.codelutin.com/issues/4991
659         if (getModel().getFishingOperation() == null) {
660             clearValidators();
661         } else {
662             registerValidators(ui.getValidator());
663         }
664     }
665 
666     @Override
667     public boolean onRemoveTab() {
668         return false;
669     }
670 
671     //------------------------------------------------------------------------//
672     //-- Public methods                                                     --//
673     //------------------------------------------------------------------------//
674 
675     public void registerValidator() {
676         registerValidators(ui.getValidator());
677     }
678 
679     public AbstractCaracteristicTabUIModel[] getSubModels() {
680         return new AbstractCaracteristicTabUIModel[]{
681                 ui.getGearUseFeatureTabContent().getModel(),
682                 ui.getVesselUseFeatureTabContent().getModel()
683         };
684     }
685 
686     public void uninstallStartDateListener() {
687         getModel().removePropertyChangeListener(EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_DATE, startDateListener);
688     }
689 
690     public void installStartDateListener() {
691         getModel().addPropertyChangeListener(EditFishingOperationUIModel.PROPERTY_GEAR_SHOOTING_START_DATE, startDateListener);
692     }
693 
694     public void uninstallCoordinatesListener() {
695         getModel().removePropertyChangeListener(coordinatePropertiesListener);
696     }
697 
698     public void installCoordinatesListener() {
699         getModel().addPropertyChangeListener(coordinatePropertiesListener);
700     }
701 
702     public FishingOperationsUI getParentUi() {
703         return getParentContainer(FishingOperationsUI.class);
704     }
705 
706     public TuttiBeanMonitor<EditFishingOperationUIModel> getFishingOperationMonitor() {
707         return fishingOperationMonitor;
708     }
709 
710     public String getGearLatitudeLabelText(CoordinateEditorType coordinateEditorType) {
711         return t("tutti.editFishingOperation.field.gearLatitude", coordinateEditorType.toString());
712     }
713 
714     public String getGearLongitudeLabelText(CoordinateEditorType coordinateEditorType) {
715         return t("tutti.editFishingOperation.field.gearLongitude", coordinateEditorType.toString());
716     }
717 
718     public String getTrawlDistanceTooltipText(Integer trawlDistance) {
719         return trawlDistance == null ?
720                 null :
721                 t("tutti.editFishingOperation.field.trawlDistance.inMilles.tip", Distances.getDistanceInMilles(Float.valueOf(trawlDistance)));
722     }
723 
724     public String getDuration(Date startDate, Date endDate) {
725         return DateTimes.getDuration(
726                 startDate,
727                 endDate,
728                 t("tutti.editFishingOperation.duration.format")
729         );
730     }
731 
732     public String decorateVessel(Vessel vessel) {
733         return decorate(vessel);
734     }
735 
736     //------------------------------------------------------------------------//
737     //-- Protected methods                                                  --//
738     //------------------------------------------------------------------------//
739 
740     protected void onSelectedStrata(EditFishingOperationUIModel model,
741                                     TuttiLocation newStrata) {
742 
743         // reset substrata value
744         model.setSubStrata(null);
745 
746         // reset sub strata combo
747         ui.getSubStrataComboBox().setData(null);
748 
749         TuttiLocation zone = getDataContext().getProgram().getZone();
750 
751         String zoneId = zone.getId();
752         String strataId = newStrata == null ? null : newStrata.getId();
753 
754         List<TuttiLocation> subStrata =
755                 getPersistenceService().getAllFishingOperationSubStrata(zoneId, strataId);
756 
757         List<TuttiLocation> location =
758                 getPersistenceService().getAllFishingOperationLocation(zoneId, strataId, null);
759 
760         ui.getSubStrataComboBox().setData(Lists.newArrayList(subStrata));
761 
762         ui.getLocationComboBox().setData(Lists.newArrayList(location));
763 
764         if (newStrata == null) {
765 
766             // reset strata, keep focus on it
767             ui.getStrataComboBox().grabFocus();
768 
769         } else {
770 
771             // try to load substrata
772 
773 
774             if (CollectionUtils.isEmpty(subStrata)) {
775 
776                 // try to load localite
777                 ui.getLocationComboBox().grabFocus();
778             }
779         }
780     }
781 
782     protected void onSelectedSubStrata(EditFishingOperationUIModel model,
783                                        TuttiLocation newSubStrata) {
784         // reset localite value
785         model.setLocation(null);
786 
787         // reset localite combo
788         ui.getLocationComboBox().setData(null);
789 
790         TuttiLocation zone = getDataContext().getProgram().getZone();
791         String zoneId = zone.getId();
792 
793         TuttiLocation strata = model.getStrata();
794         String subStrataId = newSubStrata == null ? null : newSubStrata.getId();
795 
796         String strataId = strata == null ? null : strata.getId();
797 
798         List<TuttiLocation> location =
799                 getPersistenceService().getAllFishingOperationLocation(zoneId, strataId, subStrataId);
800 
801         ui.getLocationComboBox().setData(Lists.newArrayList(location));
802 
803         if (newSubStrata == null) {
804 
805             // reset substrata, keep focus on it
806             ui.getSubStrataComboBox().grabFocus();
807         }
808     }
809 
810     protected boolean areAllModelsValid() {
811         boolean result = getModel().isValid();
812         AbstractCaracteristicTabUIModel[] subModels = getSubModels();
813         for (AbstractCaracteristicTabUIModel subModel : subModels) {
814             result &= subModel.isValid();
815         }
816         return result;
817     }
818 
819     protected boolean isAModelModified() {
820         boolean result = getModel().isModify();
821         AbstractCaracteristicTabUIModel[] subModels = getSubModels();
822         for (AbstractCaracteristicTabUIModel subModel : subModels) {
823             result |= subModel.isModify();
824         }
825         return result;
826     }
827 
828     public void resetAllModels() {
829 
830         for (AbstractCaracteristicTabUIModel subModel : getSubModels()) {
831             subModel.setModify(false);
832         }
833         getModel().setModify(false);
834     }
835 
836 }