View Javadoc
1   package fr.ifremer.dali.ui.swing.content.manage.program.programs;
2   
3   /*
4    * #%L
5    * Dali :: UI
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2014 - 2015 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   *
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   *
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  
26  import fr.ifremer.dali.dto.DaliBeans;
27  import fr.ifremer.dali.dto.configuration.programStrategy.ProgramDTO;
28  import fr.ifremer.dali.service.DaliTechnicalException;
29  import fr.ifremer.dali.ui.swing.content.manage.program.ProgramsUI;
30  import fr.ifremer.dali.ui.swing.content.manage.program.programs.departments.DepartmentsDialogUI;
31  import fr.ifremer.dali.ui.swing.content.manage.program.programs.users.UsersDialogUI;
32  import fr.ifremer.dali.ui.swing.util.DaliUIs;
33  import fr.ifremer.dali.ui.swing.util.table.AbstractDaliTableModel;
34  import fr.ifremer.dali.ui.swing.util.table.AbstractDaliTableUIHandler;
35  import fr.ifremer.quadrige3.ui.swing.action.ActionFactory;
36  import fr.ifremer.quadrige3.ui.swing.table.SwingTable;
37  import jaxx.runtime.validator.swing.SwingValidator;
38  import org.apache.commons.lang3.StringUtils;
39  import org.jdesktop.swingx.table.TableColumnExt;
40  
41  import javax.swing.SortOrder;
42  import javax.swing.SwingUtilities;
43  import javax.swing.SwingWorker;
44  import java.awt.event.MouseAdapter;
45  import java.awt.event.MouseEvent;
46  import java.util.Collection;
47  import java.util.List;
48  import java.util.concurrent.ExecutionException;
49  import java.util.concurrent.ThreadPoolExecutor;
50  
51  import static org.nuiton.i18n.I18n.t;
52  
53  /**
54   * Controleur pour la zone des programmes.
55   */
56  public class ProgramsTableUIHandler extends AbstractDaliTableUIHandler<ProgramsTableRowModel, ProgramsTableUIModel, ProgramsTableUI> {
57  
58      private ThreadPoolExecutor executor;
59      private boolean newRowSelected;
60  
61      /**
62       * <p>Constructor for ProgramsTableUIHandler.</p>
63       */
64      public ProgramsTableUIHandler() {
65          super(ProgramsTableRowModel.PROPERTY_NAME,
66                  ProgramsTableRowModel.PROPERTY_DESCRIPTION,
67                  ProgramsTableRowModel.PROPERTY_COMMENT,
68                  ProgramsTableRowModel.PROPERTY_CODE);
69      }
70  
71      /**
72       * {@inheritDoc}
73       */
74      @Override
75      protected String[] getRowPropertiesToIgnore() {
76          return new String[]{ProgramsTableRowModel.PROPERTY_STRATEGIES, ProgramsTableRowModel.PROPERTY_STRATEGIES_LOADED,
77                  ProgramsTableRowModel.PROPERTY_LOCATIONS, ProgramsTableRowModel.PROPERTY_LOCATIONS_LOADED,
78                  ProgramsTableRowModel.PROPERTY_ERRORS};
79      }
80  
81      /**
82       * {@inheritDoc}
83       */
84      @Override
85      public AbstractDaliTableModel<ProgramsTableRowModel> getTableModel() {
86          return (ProgramsTableModel) getTable().getModel();
87      }
88  
89      /**
90       * {@inheritDoc}
91       */
92      @Override
93      public SwingTable getTable() {
94          return getUI().getProgramsTable();
95      }
96  
97      /**
98       * {@inheritDoc}
99       */
100     @Override
101     public void beforeInit(final ProgramsTableUI ui) {
102         super.beforeInit(ui);
103 
104         // create model and register to the JAXX context
105         final ProgramsTableUIModel model = new ProgramsTableUIModel();
106         ui.setContextValue(model);
107     }
108 
109     /**
110      * {@inheritDoc}
111      */
112     @Override
113     public void afterInit(ProgramsTableUI ui) {
114 
115         // Initialiser l UI
116         initUI(ui);
117 
118         // Initialiser le tableau
119         initTable();
120 
121         // Ajout des listeners
122         initListeners();
123 
124         // Initilisation du bouton editer
125         initActionComboBox(getUI().getEditCombobox());
126 
127         // Register validator
128         registerValidators(getValidator());
129         listenValidatorValid(getValidator(), getModel());
130 
131         executor = ActionFactory.createSingleThreadExecutor(ActionFactory.ExecutionMode.LATEST);
132 
133     }
134 
135     @Override
136     public void onCloseUI() {
137         executor.shutdownNow();
138         super.onCloseUI();
139     }
140 
141     /**
142      * {@inheritDoc}
143      */
144     @Override
145     public boolean isRowValid(final ProgramsTableRowModel row) {
146         row.getErrors().clear();
147 
148         return !row.isEditable() || (super.isRowValid(row) && isProgramValid(row));
149     }
150 
151     private boolean isProgramValid(ProgramsTableRowModel row) {
152 
153         // check if a national program has at least one manager
154         if (row.sizeManagerPersons() == 0) {
155             DaliBeans.addError(row, t("dali.programs.validation.error.noManager"), ProgramsTableRowModel.PROPERTY_CODE);
156         }
157 
158         // check if at least one location is set
159         if (row.isLocationsLoaded() && row.isLocationsEmpty()) {
160             DaliBeans.addError(row, t("dali.programs.validation.error.noLocationOnProg"), ProgramsTableRowModel.PROPERTY_CODE);
161         }
162 
163         // check sub-models
164         if (!row.isStrategiesValid() || !row.isLocationsValid()) {
165             DaliBeans.addError(row, t("dali.program.tables.error"), ProgramsTableRowModel.PROPERTY_CODE);
166         }
167 
168         return row.isErrorsEmpty();
169     }
170 
171     /**
172      * load programs in table
173      *
174      * @param programs programs to load
175      */
176     public void loadPrograms(Collection<ProgramDTO> programs) {
177 
178         getModel().setSingleSelectedRow(null);
179         getModel().setBeans(programs);
180 
181         getModel().getRows().forEach(programsTableRowModel ->
182                 {
183                     // a program is editable if save is enabled
184                     programsTableRowModel.setEditable(getModel().isSaveEnabled());
185 
186                     // determinate if current user is manager
187                     programsTableRowModel.setCurrentUserIsManager(
188                             programsTableRowModel.getManagerPersons().stream().anyMatch(personDTO -> personDTO.getId().equals(getContext().getDataContext().getRecorderPersonId()))
189                     );
190                 });
191 
192         recomputeRowsValidState();
193         autoSelectRow();
194     }
195 
196     /**
197      * {@inheritDoc}
198      */
199     @Override
200     protected void onRowModified(int rowIndex, ProgramsTableRowModel row, String propertyName, Integer propertyIndex, Object oldValue, Object newValue) {
201         super.onRowModified(rowIndex, row, propertyName, propertyIndex, oldValue, newValue);
202 
203         row.setDirty(true);
204     }
205 
206     /**
207      * Initialisation des listeners.
208      */
209     private void initListeners() {
210 
211         // Listener sur le tableau
212         getModel().addPropertyChangeListener(ProgramsTableUIModel.PROPERTY_SINGLE_ROW_SELECTED, evt -> {
213 
214             final ProgramsTableRowModel selectedProgram = getModel().getSingleSelectedRow();
215 
216             // Si un seul element a ete selectionne
217             if (selectedProgram != null) {
218 
219                 newRowSelected = true;
220 
221                 // Suppression des informations du contexte
222                 getContext().setSelectedProgramCode(selectedProgram.getCode());
223 
224                 getModel().getParentModel().getStrategiesUIModel().setLoading(true);
225                 getModel().getParentModel().getLocationsUIModel().setLoading(true);
226                 executor.execute(new StrategyAndLocationLoader(selectedProgram));
227 
228             }
229         });
230 
231         // add a click listener on table to handle unselect strategy table
232         getTable().addMouseListener(new MouseAdapter() {
233             @Override
234             public void mouseClicked(MouseEvent e) {
235                 if (!newRowSelected) {
236                     getModel().firePropertyChanged(ProgramsTableUIModel.PROPERTY_SINGLE_ROW_SELECTED, null, null);
237                 }
238                 newRowSelected = false;
239             }
240 
241         });
242 
243         // Add listener on saveEnabled property to disable change (Mantis #47532)
244         getModel().addPropertyChangeListener(ProgramsTableUIModel.PROPERTY_SAVE_ENABLED, evt -> {
245             getModel().getRows().forEach(programsTableRowModel -> programsTableRowModel.setEditable(getModel().isSaveEnabled()));
246             getUI().getProgramsTable().invalidate();
247             getUI().getProgramsTable().repaint();
248             autoSelectRow();
249         });
250 
251         getModel().addPropertyChangeListener(ProgramsTableUIModel.EVENT_SAVE_STRATEGIES, evt -> {
252             if (getModel().getSingleSelectedRow() != null) {
253                 getModel().getSingleSelectedRow().setStrategies(getModel().getParentModel().getStrategiesUIModel().getBeans());
254                 getModel().getSingleSelectedRow().setStrategiesLoaded(true);
255             }
256         });
257 
258         //noinspection unchecked
259         getModel().addPropertyChangeListener(ProgramsTableUIModel.EVENT_REMOVE_LOCATIONS, evt -> removeLocations((List<Integer>) evt.getNewValue()));
260         getModel().addPropertyChangeListener(ProgramsTableUIModel.EVENT_RESELECT, evt -> autoSelectRow());
261     }
262 
263     /**
264      * <p>removeLocations.</p>
265      *
266      * @param locationIds a {@link java.util.List} object.
267      */
268     public void removeLocations(List<Integer> locationIds) {
269         ProgramsTableRowModel programModel = getModel().getSingleSelectedRow();
270         if (programModel != null) {
271             programModel.getLocations().removeIf(location -> locationIds.contains(location.getId()));
272             recomputeRowValidState(programModel);
273         }
274     }
275 
276     /**
277      * <p>showManagers.</p>
278      */
279     public void showManagers() {
280 
281         if (getModel().getSingleSelectedRow() == null) {
282             return;
283         }
284 
285         UsersDialogUI usersDialogUI = new UsersDialogUI(getContext());
286         usersDialogUI.getModel().setManagerList(true);
287         usersDialogUI.getModel().setProgram(getModel().getSingleSelectedRow());
288 
289         openDialog(usersDialogUI);
290 
291     }
292 
293     /**
294      * <p>showRecorderPersons.</p>
295      */
296     public void showRecorderPersons() {
297 
298         if (getModel().getSingleSelectedRow() == null) {
299             return;
300         }
301 
302         UsersDialogUI usersDialogUI = new UsersDialogUI(getContext());
303         usersDialogUI.getModel().setManagerList(false);
304         usersDialogUI.getModel().setProgram(getModel().getSingleSelectedRow());
305 
306         openDialog(usersDialogUI);
307 
308     }
309 
310     /**
311      * <p>showRecorderDepartments.</p>
312      */
313     public void showRecorderDepartments() {
314 
315         if (getModel().getSingleSelectedRow() == null) {
316             return;
317         }
318 
319         DepartmentsDialogUI usersDialogUI = new DepartmentsDialogUI(getContext());
320         usersDialogUI.getModel().setProgram(getModel().getSingleSelectedRow());
321 
322         openDialog(usersDialogUI);
323 
324     }
325 
326     /**
327      * Initialisation de le tableau.
328      */
329     private void initTable() {
330 
331         // La table des prelevements
332         final SwingTable table = getTable();
333 
334         // Column code
335         final TableColumnExt columnCode = addColumn(
336                 ProgramsTableModel.CODE);
337         columnCode.setSortable(true);
338         columnCode.setEditable(false);
339 
340         // Column libelle
341         final TableColumnExt columnLibelle = addColumn(
342                 ProgramsTableModel.NAME);
343         columnLibelle.setSortable(true);
344 
345         // Column description
346         TableColumnExt columnDescription = addCommentColumn(ProgramsTableModel.DESCRIPTION, ProgramDTO.PROPERTY_DESCRIPTION, true);
347 
348         // Comment, creation and update dates
349         addCommentColumn(ProgramsTableModel.COMMENT);
350         TableColumnExt creationDateCol = addDatePickerColumnToModel(ProgramsTableModel.CREATION_DATE, getConfig().getDateTimeFormat(), false);
351         fixColumnWidth(creationDateCol, 120);
352         TableColumnExt updateDateCol = addDatePickerColumnToModel(ProgramsTableModel.UPDATE_DATE, getConfig().getDateTimeFormat(), false);
353         fixColumnWidth(updateDateCol, 120);
354 
355         final TableColumnExt departmentHermeticCol = addBooleanColumnToModel(ProgramsTableModel.DEPARTMENT_HERMETIC, table);
356         fixColumnWidth(departmentHermeticCol, DaliUIs.DALI_CHECKBOX_WIDTH);
357         departmentHermeticCol.setSortable(true);
358 
359         // Modele de la table
360         final ProgramsTableModel tableModel = new ProgramsTableModel(getTable().getColumnModel());
361         table.setModel(tableModel);
362 
363         // Les columns obligatoire sont toujours presentes
364         columnCode.setHideable(false);
365         columnLibelle.setHideable(false);
366         columnDescription.setHideable(false);
367         departmentHermeticCol.setHideable(false);
368 
369         // Initialisation de la table
370         initTable(table);
371 
372         creationDateCol.setVisible(false);
373         updateDateCol.setVisible(false);
374 
375         // Number rows visible
376         table.setVisibleRowCount(4);
377 
378         // Tri par defaut
379         table.setSortOrder(ProgramsTableModel.CODE, SortOrder.ASCENDING);
380     }
381 
382     /**
383      * Selection d une ligne dans le tableau suivant l identifiant de la ligne.
384      */
385     public void autoSelectRow() {
386 
387         String previousSelectedProgramCode = getContext().getSelectProgramCode();
388 
389         if (StringUtils.isBlank(previousSelectedProgramCode) && getModel().getRowCount() > 1) {
390             return;
391         }
392 
393         // force deselection first
394         getModel().setSingleSelectedRow(null);
395 
396         ProgramsTableRowModel rowToSelect = null;
397         if (getModel().getRowCount() == 1) {
398 
399             // unique row
400             rowToSelect = getModel().getRows().get(0);
401 
402         } else {
403 
404             // find row with the previous selected program code
405             for (final ProgramsTableRowModel row : getModel().getRows()) {
406                 if (row.getCode().equals(previousSelectedProgramCode)) {
407                     rowToSelect = row;
408                     break;
409                 }
410             }
411         }
412 
413         // Selected row
414         if (rowToSelect != null) {
415             ProgramsTableRowModel finalRowToSelect = rowToSelect;
416             SwingUtilities.invokeLater(() -> {
417                 selectRow(finalRowToSelect);
418                 getModel().setSingleSelectedRow(finalRowToSelect);
419             });
420         }
421 
422     }
423 
424     /**
425      * {@inheritDoc}
426      */
427     @Override
428     public SwingValidator<ProgramsTableUIModel> getValidator() {
429         return getUI().getValidator();
430     }
431 
432     private class StrategyAndLocationLoader extends SwingWorker<Object, Object> {
433 
434         private final ProgramsUI adminProgrammeUI = getUI().getParentContainer(ProgramsUI.class);
435         private final ProgramsTableRowModel selectedProgram;
436 
437         StrategyAndLocationLoader(ProgramsTableRowModel selectedProgram) {
438             this.selectedProgram = selectedProgram;
439         }
440 
441         @Override
442         protected Object doInBackground() {
443 
444             getContext().getProgramStrategyService().loadStrategiesAndLocations(selectedProgram);
445 
446             return null;
447         }
448 
449         @Override
450         protected void done() {
451             if (isCancelled()) {
452                 return;
453             }
454             try {
455                 get();
456             } catch (InterruptedException | ExecutionException e) {
457                 throw new DaliTechnicalException(e.getMessage(), e);
458             }
459 
460             // Update strategies table
461             adminProgrammeUI.getStrategiesTableUI().getHandler().load(selectedProgram);
462 
463             // Update applied strategies table
464             adminProgrammeUI.getLocationsTableUI().getHandler().loadMonitoringLocationsFromProgram(selectedProgram);
465 
466             // Suppression des PSFMs
467             adminProgrammeUI.getModel().getPmfmsUIModel().clear();
468 
469             // revalidate program
470             recomputeRowValidState(selectedProgram);
471             adminProgrammeUI.getHandler().getValidator().doValidate();
472 
473         }
474     }
475 }