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