1 package fr.ifremer.dali.ui.swing.content.observation.photo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 import fr.ifremer.dali.dto.DaliBeans;
27 import fr.ifremer.dali.dto.data.photo.PhotoDTO;
28 import fr.ifremer.dali.dto.data.sampling.SamplingOperationDTO;
29 import fr.ifremer.dali.service.DaliTechnicalException;
30 import fr.ifremer.dali.ui.swing.util.image.PhotoViewer;
31 import fr.ifremer.dali.ui.swing.util.table.AbstractDaliTableUIHandler;
32 import fr.ifremer.dali.ui.swing.util.table.AbstractDaliTableUIModel;
33 import fr.ifremer.quadrige3.core.dao.technical.Images;
34 import fr.ifremer.quadrige3.ui.swing.table.SwingTable;
35 import fr.ifremer.quadrige3.ui.swing.table.editor.ExtendedComboBoxCellEditor;
36 import jaxx.runtime.SwingUtil;
37 import jaxx.runtime.validator.swing.SwingValidator;
38 import org.apache.commons.lang3.StringUtils;
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.jdesktop.swingx.table.TableColumnExt;
42 import org.nuiton.jaxx.application.swing.tab.TabHandler;
43
44 import javax.swing.SwingWorker;
45 import javax.swing.table.TableCellRenderer;
46 import java.util.ArrayList;
47 import java.util.List;
48 import java.util.Objects;
49 import java.util.concurrent.ExecutionException;
50
51 import static org.nuiton.i18n.I18n.t;
52
53
54
55
56 public class PhotosTabUIHandler extends AbstractDaliTableUIHandler<PhotosTableRowModel, PhotosTabUIModel, PhotosTabUI> implements TabHandler {
57
58
59
60
61 private static final Log LOG = LogFactory.getLog(PhotosTabUIHandler.class);
62
63 private ExtendedComboBoxCellEditor<SamplingOperationDTO> samplingOperationCellEditor;
64
65
66
67
68 @Override
69 public void beforeInit(final PhotosTabUI ui) {
70 super.beforeInit(ui);
71
72
73 final PhotosTabUIModel model = new PhotosTabUIModel();
74 ui.setContextValue(model);
75 }
76
77
78
79
80 @Override
81 public void afterInit(final PhotosTabUI ui) {
82 initUI(ui);
83
84
85 SwingUtil.setLayerUI(getUI().getPhotoViewer(), getUI().getPhotoBlockLayer());
86
87
88 initTable();
89
90
91 initListeners();
92
93
94 registerValidators(getValidator());
95 listenValidatorValid(getValidator(), getModel());
96
97
98 ui.getDownloadPhotoButton().setEnabled(false);
99 ui.getExportPhotoButton().setEnabled(false);
100 }
101
102
103
104
105 @Override
106 protected void onRowModified(int rowIndex, PhotosTableRowModel row, String propertyName, Integer propertyIndex, Object oldValue, Object newValue) {
107 super.onRowModified(rowIndex, row, propertyName, propertyIndex, oldValue, newValue);
108
109 row.setDirty(true);
110 }
111
112
113
114
115 @Override
116 protected String[] getRowPropertiesToIgnore() {
117 return new String[]{PhotoDTO.PROPERTY_DIRTY};
118 }
119
120
121
122
123 @Override
124 protected boolean isRowValid(PhotosTableRowModel row) {
125
126 return super.isRowValid(row) && isPhotoValid(row);
127 }
128
129 private boolean isPhotoValid(PhotosTableRowModel row) {
130
131
132 if (getModel().getRowCount() >= 2) {
133 for (PhotosTableRowModel otherRow : getModel().getRows()) {
134 if (row == otherRow) continue;
135 if (StringUtils.isNotBlank(row.getName()) && row.getName().equals(otherRow.getName())) {
136
137 DaliBeans.addError(row, t("dali.photo.name.duplicate"), PhotosTableRowModel.PROPERTY_NAME);
138 }
139 }
140 }
141
142 return DaliBeans.hasNoBlockingError(row);
143 }
144
145
146
147
148 private void initTable() {
149
150
151 final TableColumnExt nameCol = addColumn(PhotosTableModel.NAME);
152 nameCol.setSortable(true);
153 nameCol.setMinWidth(100);
154
155
156 final TableColumnExt typeCol = addFilterableComboDataColumnToModel(PhotosTableModel.TYPE,
157 getContext().getReferentialService().getPhotoTypes(), false);
158 typeCol.setSortable(true);
159 typeCol.setMinWidth(100);
160
161
162 final TableColumnExt captionCol = addColumn(PhotosTableModel.CAPTION);
163 captionCol.setSortable(true);
164 captionCol.setMinWidth(100);
165
166
167 final TableColumnExt dateCol = addDatePickerColumnToModel(PhotosTableModel.DATE, getConfig().getDateFormat());
168 dateCol.setSortable(true);
169 dateCol.setMinWidth(100);
170
171
172 samplingOperationCellEditor = newExtendedComboBoxCellEditor(null, PhotosTableModel.SAMPLING_OPERATION, false);
173 final TableColumnExt samplingCol = addColumn(
174 samplingOperationCellEditor,
175 newTableCellRender(PhotosTableModel.SAMPLING_OPERATION),
176 PhotosTableModel.SAMPLING_OPERATION);
177 samplingCol.setSortable(true);
178 samplingCol.setMinWidth(200);
179
180
181 final TableColumnExt directionCol = addColumn(PhotosTableModel.DIRECTION);
182 directionCol.setSortable(true);
183 directionCol.setMinWidth(200);
184
185
186 final TableColumnExt pathCol = addColumn(
187 null,
188 (table, value, isSelected, hasFocus, row, column) -> {
189
190 if (!getTableModel().getEntry(getTable().convertRowIndexToModel(row)).isFileExists()) {
191 value = t("dali.photo.unavailable");
192 }
193
194 TableCellRenderer defaultRenderer = table.getDefaultRenderer(PhotosTableModel.PATH.getPropertyType());
195 return defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
196 },
197 PhotosTableModel.PATH);
198 pathCol.setSortable(true);
199 pathCol.setMinWidth(200);
200 pathCol.setEditable(false);
201
202
203 final PhotosTableModel tableModel = new PhotosTableModel(getTable().getColumnModel());
204 getTable().setModel(tableModel);
205
206
207 nameCol.setHideable(false);
208 samplingCol.setHideable(false);
209
210
211 initTable(getTable());
212
213
214 directionCol.setVisible(false);
215 pathCol.setVisible(false);
216
217 getTable().setVisibleRowCount(5);
218
219
220 addEditionPanelBorder();
221 }
222
223
224
225
226 @Override
227 public SwingValidator<PhotosTabUIModel> getValidator() {
228 return getUI().getValidator();
229 }
230
231
232
233
234 private void load() {
235
236
237 samplingOperationCellEditor.getCombo().setData(new ArrayList<>(getModel().getObservationModel().getSamplingOperations()));
238
239
240 final List<PhotoDTO> photos = getModel().getObservationModel().getPhotos();
241
242
243 getTableModel().setReadOnly(!getModel().getObservationModel().isEditable());
244 getModel().setBeans(photos);
245 recomputeRowsValidState();
246
247 if (getModel().getPhotoIndex() == null && getModel().getRowCount() > 0) {
248 getModel().setPhotoIndex(getModel().getFirstPhotoIndex());
249 }
250
251 }
252
253
254
255
256 void removePhoto() {
257
258 if (getModel().getSelectedRows().isEmpty()) {
259 LOG.warn("No selected photo");
260 return;
261 }
262
263
264 if (askBeforeDelete(t("dali.action.delete.photos.title"), t("dali.action.delete.photos.message"))) {
265
266 getModel().deleteSelectedRows();
267
268 updatePhotoViewerContent(false);
269
270 getModel().setModify(true);
271 }
272 }
273
274
275
276
277 void firstPhoto() {
278 if (!getModel().getPhotoIndex().equals(getModel().getFirstPhotoIndex())) {
279 getModel().setPhotoIndex(getModel().getFirstPhotoIndex());
280 }
281 }
282
283
284
285
286 void previousPhoto() {
287 if (!getModel().getPhotoIndex().equals(getModel().getFirstPhotoIndex())) {
288 getModel().setPhotoIndex(getModel().getPhotoIndex() - 1);
289 }
290 }
291
292
293
294
295 void nextPhoto() {
296 if (!getModel().getPhotoIndex().equals(getModel().getLastPhotoIndex())) {
297 getModel().setPhotoIndex(getModel().getPhotoIndex() + 1);
298 }
299 }
300
301
302
303
304 void lastPhoto() {
305 if (!getModel().getPhotoIndex().equals(getModel().getLastPhotoIndex())) {
306 getModel().setPhotoIndex(getModel().getLastPhotoIndex());
307 }
308 }
309
310 public void fullScreen() {
311 if (getModel().getPhotoIndex() == null) return;
312 PhotosTableRowModel selectedPhoto = getModel().getSelectedPhoto();
313 if (selectedPhoto == null) return;
314
315 PhotoViewer<PhotoDTO> photoViewer = new PhotoViewer<>();
316
317 photoViewer.setPhoto(selectedPhoto, Images.ImageType.BASE);
318 openFrame(photoViewer, selectedPhoto.getName(), null, getUI().getBounds());
319 }
320
321
322
323
324 private void initListeners() {
325
326 getModel().addPropertyChangeListener(PhotosTabUIModel.PROPERTY_OBSERVATION_MODEL, evt -> load());
327
328 getModel().addPropertyChangeListener(PhotosTabUIModel.PROPERTY_PHOTO_INDEX, evt -> {
329 if (getModel().isModelAdjusting())
330 return;
331
332 PhotosTableRowModel selectedPhoto = getModel().getSelectedPhoto();
333 if (selectedPhoto != null) {
334 setFocusOnCell(selectedPhoto);
335 }
336 });
337
338
339 getUI().getPhotoViewer().addPropertyChangeListener(PhotoViewer.EVENT_PHOTO_CLICKED, evt -> {
340
341 if (evt.getNewValue() instanceof PhotoDTO) {
342
343 if (getModel().isModelAdjusting()) {
344 return;
345 }
346 try {
347
348 getModel().setModelAdjusting(true);
349
350 PhotoDTO photo = (PhotoDTO) evt.getNewValue();
351
352 PhotosTableRowModel rowModel = null;
353 if (photo instanceof PhotosTableRowModel) {
354 rowModel = (PhotosTableRowModel) photo;
355 } else {
356 for (PhotosTableRowModel row : getModel().getRows()) {
357 if (Objects.equals(row.getFullPath(), photo.getFullPath())) {
358 rowModel = row;
359 break;
360 }
361 }
362 }
363
364 if (rowModel != null) {
365
366 if (rowModel.isFileDownloadable()) {
367
368
369 DownloadAction downloadAction = getContext().getActionFactory().createLogicAction(this, DownloadAction.class);
370 downloadAction.setToDownload(rowModel);
371 getContext().getActionEngine().runAction(downloadAction);
372
373 } else if (rowModel != getModel().getSingleSelectedRow()) {
374
375
376 setFocusOnCell(rowModel);
377 getModel().setPhotoIndex(getTableModel().getRowIndex(rowModel));
378 }
379 }
380
381 } finally {
382
383 getModel().setModelAdjusting(false);
384 }
385 }
386 });
387
388
389 getModel().addPropertyChangeListener(AbstractDaliTableUIModel.PROPERTY_SELECTED_ROWS, evt -> {
390
391 if (getModel().isModelAdjusting())
392 return;
393
394 getModel().setModelAdjusting(true);
395 getModel().setPhotoIndex(getTableModel().getRowIndex(getModel().getSingleSelectedRow()));
396 updatePhotoViewerContent(true);
397 getModel().setModelAdjusting(false);
398
399 });
400
401
402 getUI().getTypeDiaporamaComboBox().addActionListener(e -> updatePhotoViewerContent(false));
403
404 }
405
406
407
408
409 @Override
410 public boolean onHideTab(int currentIndex, int newIndex) {
411 return true;
412 }
413
414
415
416
417 @Override
418 public void onShowTab(int currentIndex, int newIndex) {
419
420 }
421
422
423
424
425 @Override
426 public boolean onRemoveTab() {
427 return false;
428 }
429
430
431
432
433 public void saveActualModel() {
434 getModel().setModelAdjusting(true);
435 getModel().getObservationModel().setPhotos(getModel().getBeans());
436 getModel().setModelAdjusting(false);
437 }
438
439
440
441
442 @Override
443 public PhotosTableModel getTableModel() {
444 return (PhotosTableModel) getTable().getModel();
445 }
446
447
448
449
450 @Override
451 public SwingTable getTable() {
452 return getUI().getPhotoTable();
453 }
454
455
456
457
458
459
460 public void updatePhotoViewerContent(boolean selectionOnly) {
461
462
463 Integer photoIndex = getModel().getPhotoIndex();
464 if (photoIndex != null) {
465 photoIndex = Math.min(photoIndex, getModel().getLastPhotoIndex());
466 photoIndex = photoIndex == -1 ? null : photoIndex;
467 if (!Objects.equals(getModel().getPhotoIndex(), photoIndex)) {
468 getModel().setPhotoIndex(photoIndex);
469 return;
470 }
471 }
472
473 PhotoLoader worker = new PhotoLoader(selectionOnly);
474 getModel().setLoading(true);
475 worker.execute();
476
477 }
478
479 private class PhotoLoader extends SwingWorker<Object, Object> {
480
481 private final boolean selectionOnly;
482
483 private PhotoLoader(boolean selectionOnly) {
484 this.selectionOnly = selectionOnly;
485 }
486
487 @Override
488 protected Object doInBackground() {
489
490 PhotoViewType viewType = getUI().getTypeDiaporamaComboBox().getSelectedItem();
491
492 if (PhotoViewType.VIEW_DIAPO.equals(viewType)) {
493
494 getUI().getPhotoViewer().setPhoto(getModel().getSingleSelectedRow(), viewType.getImageType());
495
496 } else if (PhotoViewType.VIEW_THUMBNAIL.equals(viewType)) {
497
498 if (!selectionOnly) {
499
500 getUI().getPhotoViewer().setPhotos(getModel().getRows(), viewType.getImageType());
501 }
502 getUI().getPhotoViewer().setSelected(getModel().getSelectedRows());
503
504 }
505
506 return null;
507 }
508
509 @Override
510 protected void done() {
511 try {
512 get();
513 } catch (InterruptedException | ExecutionException e) {
514 throw new DaliTechnicalException(e.getMessage(), e);
515 }
516
517
518 getUI().getPhotoIndexLabel().setText(String.format("%s / %s", getModel().getPhotoIndex() == null ? 0 : getModel().getPhotoIndex() + 1, getModel().getRowCount()));
519
520
521 getUI().getFirstPhotoButton().setEnabled(getModel().getPhotoIndex() != null && getModel().getPhotoIndex() != getModel().getFirstPhotoIndex());
522 getUI().getPreviousPhotoButton().setEnabled(getModel().getPhotoIndex() != null && getModel().getPhotoIndex() != getModel().getFirstPhotoIndex());
523 getUI().getNextPhotoButton().setEnabled(getModel().getPhotoIndex() != null && getModel().getPhotoIndex() != getModel().getLastPhotoIndex());
524 getUI().getLastPhotoButton().setEnabled(getModel().getPhotoIndex() != null && getModel().getPhotoIndex() != getModel().getLastPhotoIndex());
525
526 getModel().setLoading(false);
527
528 }
529 }
530 }