1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.metis.ui;
18
19 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventManager;
21 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar;
22 import io.github.tonywasher.joceanus.oceanus.event.OceanusEventRegistrar.OceanusEventProvider;
23 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceEvent;
24 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceResource;
25 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet;
26 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisBooleanPreference;
27 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisDatePreference;
28 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisEnumPreference;
29 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisIntegerPreference;
30 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisPreferenceId;
31 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisPreferenceItem;
32 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceSet.MetisStringPreference;
33 import io.github.tonywasher.joceanus.metis.preference.MetisPreferenceType;
34 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIAlignment;
35 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
36 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIConstant;
37 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIEvent;
38 import io.github.tonywasher.joceanus.tethys.api.base.TethysUIGenericWrapper;
39 import io.github.tonywasher.joceanus.tethys.api.button.TethysUIButton;
40 import io.github.tonywasher.joceanus.tethys.api.control.TethysUICheckBox;
41 import io.github.tonywasher.joceanus.tethys.api.control.TethysUIControlFactory;
42 import io.github.tonywasher.joceanus.tethys.api.control.TethysUILabel;
43 import io.github.tonywasher.joceanus.tethys.api.dialog.TethysUIDirectorySelector;
44 import io.github.tonywasher.joceanus.tethys.api.dialog.TethysUIFileSelector;
45 import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
46 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIColorButtonField;
47 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIDateButtonField;
48 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIIntegerEditField;
49 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIScrollButtonField;
50 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIDataEditField.TethysUIStringEditField;
51 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIFieldAttribute;
52 import io.github.tonywasher.joceanus.tethys.api.field.TethysUIFieldType;
53 import io.github.tonywasher.joceanus.tethys.api.menu.TethysUIScrollMenu;
54 import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIBorderPaneManager;
55 import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIFlowPaneManager;
56 import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIGridPaneManager;
57 import io.github.tonywasher.joceanus.tethys.api.pane.TethysUIPaneFactory;
58
59 import java.io.File;
60 import java.util.ArrayList;
61 import java.util.List;
62 import java.util.function.Predicate;
63
64
65
66
67 public class MetisPreferenceSetView
68 implements OceanusEventProvider<MetisPreferenceEvent>, TethysUIComponent {
69
70
71
72 protected static final String STR_COLON = TethysUIConstant.STR_COLON;
73
74
75
76
77 private static final String NLS_PREFERENCES = MetisPreferenceResource.UI_TITLE_PREFERENCES.getValue();
78
79
80
81
82 private static final String NLS_OPTIONS = MetisPreferenceResource.UI_TITLE_OPTIONS.getValue();
83
84
85
86
87 private final OceanusEventManager<MetisPreferenceEvent> theEventManager;
88
89
90
91
92 private final MetisPreferenceSet thePreferences;
93
94
95
96
97 private final TethysUIBorderPaneManager thePane;
98
99
100
101
102 private final TethysUIGridPaneManager theGrid;
103
104
105
106
107 private final TethysUIFactory<?> theGuiFactory;
108
109
110
111
112 private final List<PreferenceElement> theElements;
113
114
115
116
117 private TethysUIFlowPaneManager theOptions;
118
119
120
121
122
123
124
125 protected MetisPreferenceSetView(final TethysUIFactory<?> pFactory,
126 final MetisPreferenceSet pPreferenceSet) {
127
128 theGuiFactory = pFactory;
129 thePreferences = pPreferenceSet;
130
131
132 theEventManager = new OceanusEventManager<>();
133
134
135 theElements = new ArrayList<>();
136
137
138 final TethysUIPaneFactory myPanes = theGuiFactory.paneFactory();
139 theGrid = myPanes.newGridPane();
140
141
142 for (MetisPreferenceItem myPref : thePreferences.getPreferences()) {
143
144 final PreferenceElement myElement = allocatePreferenceElement(myPref);
145 if (myElement != null) {
146 theElements.add(myElement);
147 }
148 }
149
150
151 thePane = myPanes.newBorderPane();
152 thePane.setBorderTitle(NLS_PREFERENCES);
153 thePane.setCentre(theGrid);
154
155
156 if (theOptions != null) {
157
158 thePane.setSouth(theOptions);
159 }
160
161
162 updateFields();
163 }
164
165
166
167
168
169
170 protected TethysUIFactory<?> getFactory() {
171 return theGuiFactory;
172 }
173
174
175
176
177
178
179 protected TethysUIGridPaneManager getGrid() {
180 return theGrid;
181 }
182
183 @Override
184 public String toString() {
185 return thePreferences.getName();
186 }
187
188 @Override
189 public TethysUIComponent getUnderlying() {
190 return thePane;
191 }
192
193 @Override
194 public OceanusEventRegistrar<MetisPreferenceEvent> getEventRegistrar() {
195 return theEventManager.getEventRegistrar();
196 }
197
198
199
200
201
202
203 boolean hasChanges() {
204 return thePreferences.hasChanges();
205 }
206
207
208
209
210 void resetChanges() {
211
212 thePreferences.resetChanges();
213
214
215 updateFields();
216 }
217
218
219
220
221
222
223 void storeChanges() throws OceanusException {
224
225 thePreferences.storeChanges();
226
227
228 updateFields();
229 }
230
231
232
233
234 private void updateFields() {
235
236 thePreferences.updateViewerEntry();
237
238
239 for (PreferenceElement myItem : theElements) {
240
241 myItem.updateField();
242 }
243 }
244
245
246
247
248 protected void notifyChanges() {
249
250 thePreferences.autoCorrectPreferences();
251
252
253 updateFields();
254
255
256 theEventManager.fireEvent(MetisPreferenceEvent.PREFCHANGED);
257 }
258
259
260
261
262
263
264
265 protected PreferenceElement allocatePreferenceElement(final MetisPreferenceItem pItem) {
266 if (pItem instanceof MetisEnumPreference<?> myEnum) {
267 return new EnumPreferenceElement<>(myEnum);
268 } else if (pItem instanceof MetisIntegerPreference myInt) {
269 return new IntegerPreferenceElement(myInt);
270 } else if (pItem instanceof MetisDatePreference myDate) {
271 return new DatePreferenceElement(myDate);
272 } else if (pItem instanceof MetisBooleanPreference myBool) {
273 return new BooleanPreferenceElement(myBool);
274 } else if (pItem instanceof MetisStringPreference myString) {
275 final MetisPreferenceId myId = myString.getType();
276 if (myId.equals(MetisPreferenceType.DIRECTORY)) {
277 return new DirectoryPreferenceElement(myString);
278 } else if (myId.equals(MetisPreferenceType.FILE)) {
279 return new FilePreferenceElement(myString);
280 } else if (myId.equals(MetisPreferenceType.COLOR)) {
281 return new ColorPreferenceElement(myString);
282 } else {
283 return new StringPreferenceElement(myString);
284 }
285 } else {
286 throw new IllegalArgumentException("Bad Preference Type: " + pItem.getType());
287 }
288 }
289
290
291
292
293 protected void determineFocus() {
294 thePreferences.setFocus();
295 }
296
297
298
299
300 @FunctionalInterface
301 protected interface PreferenceElement {
302
303
304
305 void updateField();
306 }
307
308
309
310
311 private final class StringPreferenceElement
312 implements PreferenceElement {
313
314
315
316 private final MetisStringPreference theItem;
317
318
319
320
321 private final TethysUIStringEditField theField;
322
323
324
325
326
327
328 StringPreferenceElement(final MetisStringPreference pItem) {
329
330 theItem = pItem;
331 theField = theGuiFactory.fieldFactory().newStringField();
332 theField.setEditable(true);
333
334
335 final TethysUILabel myLabel = theGuiFactory.controlFactory().newLabel(pItem.getDisplay() + STR_COLON);
336 myLabel.setAlignment(TethysUIAlignment.EAST);
337
338
339 theGrid.addCell(myLabel);
340 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
341 theGrid.addCell(theField);
342 theGrid.setCellColumnSpan(theField, 2);
343 theGrid.allowCellGrowth(theField);
344 theGrid.newRow();
345
346
347 theField.getEventRegistrar().addEventListener(e -> {
348 pItem.setValue(theField.getValue());
349 notifyChanges();
350 });
351 }
352
353 @Override
354 public void updateField() {
355
356 theField.setValue(theItem.getValue());
357
358
359 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
360 theField.adjustField();
361
362
363 theField.setEnabled(!theItem.isHidden());
364 }
365 }
366
367
368
369
370 private final class IntegerPreferenceElement
371 implements PreferenceElement {
372
373
374
375 private final MetisIntegerPreference theItem;
376
377
378
379
380 private final TethysUIIntegerEditField theField;
381
382
383
384
385 private final TethysUILabel theRangeLabel;
386
387
388
389
390
391
392 IntegerPreferenceElement(final MetisIntegerPreference pItem) {
393
394 theItem = pItem;
395 theField = theGuiFactory.fieldFactory().newIntegerField();
396 theField.setEditable(true);
397 theField.setPreferredWidth(TethysUIFieldType.INTEGER.getDefaultWidth());
398
399
400 final TethysUIControlFactory myControls = theGuiFactory.controlFactory();
401 final TethysUILabel myLabel = myControls.newLabel(pItem.getDisplay() + STR_COLON);
402 myLabel.setAlignment(TethysUIAlignment.EAST);
403
404
405 theRangeLabel = myControls.newLabel();
406
407
408 theGrid.addCell(myLabel);
409 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
410 theGrid.addCell(theField);
411 theGrid.addCell(theRangeLabel);
412 theGrid.allowCellGrowth(theRangeLabel);
413 theGrid.newRow();
414
415
416 theField.getEventRegistrar().addEventListener(e -> {
417 pItem.setValue(theField.getValue());
418 notifyChanges();
419 });
420 }
421
422 @Override
423 public void updateField() {
424
425 theField.setValue(theItem.getValue());
426
427
428 handleRange();
429
430
431 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
432 theField.adjustField();
433
434
435 theField.setEnabled(!theItem.isHidden());
436 }
437
438
439
440
441 private void handleRange() {
442
443 final Integer myMin = theItem.getMinimum();
444 final Integer myMax = theItem.getMaximum();
445 final boolean hasMin = myMin != null;
446 final boolean hasMax = myMax != null;
447
448
449 theField.setValidator(p -> (hasMin && p < myMin) || (hasMax && p > myMax)
450 ? MetisPreferenceResource.UI_RANGE_ERROR.getValue()
451 : null);
452 theRangeLabel.setText(null);
453
454
455 if (hasMin || hasMax) {
456
457 final StringBuilder myBuilder = new StringBuilder();
458
459
460 if (hasMin) {
461 myBuilder.append(MetisPreferenceResource.UI_RANGE_MIN.getValue());
462 myBuilder.append(' ');
463 myBuilder.append(myMin);
464 if (hasMax) {
465 myBuilder.append(", ");
466 }
467 }
468
469
470 if (hasMax) {
471 myBuilder.append(MetisPreferenceResource.UI_RANGE_MAX.getValue());
472 myBuilder.append(' ');
473 myBuilder.append(myMax);
474 }
475
476
477 theRangeLabel.setText(myBuilder.toString());
478 }
479 }
480 }
481
482
483
484
485 private final class BooleanPreferenceElement
486 implements PreferenceElement {
487
488
489
490 private final MetisBooleanPreference theItem;
491
492
493
494
495 private final TethysUICheckBox theCheckBox;
496
497
498
499
500
501
502 BooleanPreferenceElement(final MetisBooleanPreference pItem) {
503
504 theItem = pItem;
505 theCheckBox = theGuiFactory.controlFactory().newCheckBox(pItem.getDisplay());
506
507
508 ensureOptionsPane();
509
510
511 theOptions.addNode(theCheckBox);
512
513
514 theCheckBox.getEventRegistrar().addEventListener(TethysUIEvent.NEWVALUE, e -> {
515 pItem.setValue(theCheckBox.isSelected());
516 notifyChanges();
517 });
518 }
519
520 @Override
521 public void updateField() {
522
523 theCheckBox.setSelected(theItem.getValue());
524
525
526 theCheckBox.setChanged(theItem.isChanged());
527
528
529 theCheckBox.setEnabled(!theItem.isHidden());
530 }
531
532
533
534
535 private void ensureOptionsPane() {
536
537 if (theOptions == null) {
538
539 theOptions = theGuiFactory.paneFactory().newFlowPane();
540 theOptions.setBorderTitle(NLS_OPTIONS);
541 }
542 }
543 }
544
545
546
547
548 private final class DatePreferenceElement
549 implements PreferenceElement {
550
551
552
553 private final MetisDatePreference theItem;
554
555
556
557
558 private final TethysUIDateButtonField theField;
559
560
561
562
563
564
565 DatePreferenceElement(final MetisDatePreference pItem) {
566
567 theItem = pItem;
568 theField = theGuiFactory.fieldFactory().newDateField();
569 theField.setEditable(true);
570
571
572 final TethysUIControlFactory myControls = theGuiFactory.controlFactory();
573 final TethysUILabel myLabel = myControls.newLabel(pItem.getDisplay() + STR_COLON);
574 myLabel.setAlignment(TethysUIAlignment.EAST);
575
576
577 final TethysUILabel myStub = myControls.newLabel();
578
579
580 theGrid.addCell(myLabel);
581 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
582 theGrid.addCell(theField);
583 theGrid.addCell(myStub);
584 theGrid.allowCellGrowth(myStub);
585 theGrid.newRow();
586
587
588 theField.getEventRegistrar().addEventListener(TethysUIEvent.NEWVALUE, e -> {
589 pItem.setValue(theField.getValue());
590 notifyChanges();
591 });
592 }
593
594 @Override
595 public void updateField() {
596
597 theField.setValue(theItem.getValue());
598
599
600 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
601 theField.adjustField();
602
603
604 theField.setEnabled(!theItem.isHidden());
605 }
606 }
607
608
609
610
611
612
613 private final class EnumPreferenceElement<E extends Enum<E>>
614 implements PreferenceElement {
615
616
617
618 private final MetisEnumPreference<E> theItem;
619
620
621
622
623 private final TethysUIScrollButtonField<TethysUIGenericWrapper> theField;
624
625
626
627
628
629
630 @SuppressWarnings("unchecked")
631 EnumPreferenceElement(final MetisEnumPreference<E> pItem) {
632
633 theItem = pItem;
634 theField = theGuiFactory.fieldFactory().newScrollField(TethysUIGenericWrapper.class);
635 theField.setEditable(true);
636
637
638 final TethysUIControlFactory myControls = theGuiFactory.controlFactory();
639 final TethysUILabel myLabel = myControls.newLabel(pItem.getDisplay() + STR_COLON);
640 myLabel.setAlignment(TethysUIAlignment.EAST);
641
642
643 final TethysUILabel myStub = myControls.newLabel();
644
645
646 theGrid.addCell(myLabel);
647 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
648 theGrid.addCell(theField);
649 theGrid.addCell(myStub);
650 theGrid.allowCellGrowth(myStub);
651 theGrid.newRow();
652
653
654 theField.setMenuConfigurator(this::buildMenu);
655 final OceanusEventRegistrar<TethysUIEvent> myRegistrar = theField.getEventRegistrar();
656 myRegistrar.addEventListener(TethysUIEvent.NEWVALUE, e -> {
657 pItem.setValue((E) theField.getValue().getData());
658 notifyChanges();
659 });
660 }
661
662
663
664
665
666
667 private void buildMenu(final TethysUIScrollMenu<TethysUIGenericWrapper> pMenu) {
668
669 pMenu.removeAllItems();
670
671
672 final Predicate<E> myFilter = theItem.getFilter();
673
674
675 for (E myEnum : theItem.getValues()) {
676
677 if (myFilter.test(myEnum)) {
678
679 pMenu.addItem(new TethysUIGenericWrapper(myEnum));
680 }
681 }
682 }
683
684 @Override
685 public void updateField() {
686
687 theField.setValue(new TethysUIGenericWrapper(theItem.getValue()));
688
689
690 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
691 theField.adjustField();
692
693
694 theField.setEnabled(!theItem.isHidden());
695 }
696 }
697
698
699
700
701 private final class ColorPreferenceElement
702 implements PreferenceElement {
703
704
705
706 private final MetisStringPreference theItem;
707
708
709
710
711 private final TethysUIColorButtonField theField;
712
713
714
715
716
717
718 ColorPreferenceElement(final MetisStringPreference pItem) {
719
720 theItem = pItem;
721 theField = theGuiFactory.fieldFactory().newColorField();
722 theField.setEditable(true);
723
724
725 final TethysUIControlFactory myControls = theGuiFactory.controlFactory();
726 final TethysUILabel myLabel = myControls.newLabel(pItem.getDisplay() + STR_COLON);
727 myLabel.setAlignment(TethysUIAlignment.EAST);
728
729
730 final TethysUILabel myStub = myControls.newLabel();
731
732
733 theGrid.addCell(myLabel);
734 theGrid.setCellAlignment(myLabel, TethysUIAlignment.EAST);
735 theGrid.addCell(theField);
736 theGrid.addCell(myStub);
737 theGrid.allowCellGrowth(myStub);
738 theGrid.newRow();
739
740
741 theField.getEventRegistrar().addEventListener(e -> {
742 pItem.setValue(theField.getValue());
743 notifyChanges();
744 });
745 }
746
747 @Override
748 public void updateField() {
749
750 theField.setValue(theItem.getValue());
751
752
753 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
754 theField.adjustField();
755
756
757 theField.setEnabled(!theItem.isHidden());
758 }
759 }
760
761
762
763
764 private final class FilePreferenceElement
765 implements PreferenceElement {
766
767
768
769 private final MetisStringPreference theItem;
770
771
772
773
774 private final TethysUIStringEditField theField;
775
776
777
778
779 private final TethysUIButton theButton;
780
781
782
783
784 private TethysUIFileSelector theSelector;
785
786
787
788
789
790
791 FilePreferenceElement(final MetisStringPreference pItem) {
792
793 theItem = pItem;
794 theField = theGuiFactory.fieldFactory().newStringField();
795 theField.setEditable(true);
796
797
798 theButton = theGuiFactory.buttonFactory().newButton();
799 theButton.setTextOnly();
800 theButton.setText(pItem.getDisplay());
801
802
803 theGrid.addCell(theButton);
804 theGrid.addCell(theField);
805 theGrid.setCellColumnSpan(theField, 2);
806 theGrid.allowCellGrowth(theField);
807 theGrid.newRow();
808
809
810 theButton.getEventRegistrar().addEventListener(e -> handleDialog());
811 theField.getEventRegistrar().addEventListener(e -> {
812 pItem.setValue(theField.getValue());
813 notifyChanges();
814 });
815 }
816
817 @Override
818 public void updateField() {
819
820 theField.setValue(theItem.getValue());
821
822
823 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
824 theField.adjustField();
825
826
827 final boolean isEnabled = !theItem.isHidden();
828 theField.setEnabled(isEnabled);
829 theButton.setEnabled(isEnabled);
830 }
831
832
833
834
835 private void handleDialog() {
836 ensureSelector();
837 theSelector.setInitialFile(new File(theItem.getValue()));
838 final File myFile = theSelector.selectFile();
839 if (myFile != null) {
840 theItem.setValue(myFile.getAbsolutePath());
841 notifyChanges();
842 }
843 }
844
845
846
847
848 private void ensureSelector() {
849 if (theSelector == null) {
850 theSelector = theGuiFactory.dialogFactory().newFileSelector();
851 theSelector.setTitle(theItem.getDisplay());
852 }
853 }
854 }
855
856
857
858
859 private final class DirectoryPreferenceElement
860 implements PreferenceElement {
861
862
863
864 private final MetisStringPreference theItem;
865
866
867
868
869 private final TethysUIStringEditField theField;
870
871
872
873
874 private final TethysUIButton theButton;
875
876
877
878
879 private TethysUIDirectorySelector theSelector;
880
881
882
883
884
885
886 DirectoryPreferenceElement(final MetisStringPreference pItem) {
887
888 theItem = pItem;
889 theField = theGuiFactory.fieldFactory().newStringField();
890 theField.setEditable(true);
891
892
893 theButton = theGuiFactory.buttonFactory().newButton();
894 theButton.setTextOnly();
895 theButton.setText(pItem.getDisplay());
896
897
898 theGrid.addCell(theButton);
899 theGrid.addCell(theField);
900 theGrid.setCellColumnSpan(theField, 2);
901 theGrid.allowCellGrowth(theField);
902 theGrid.newRow();
903
904
905 theButton.getEventRegistrar().addEventListener(e -> handleDialog());
906 theField.getEventRegistrar().addEventListener(e -> {
907 pItem.setValue(theField.getValue());
908 notifyChanges();
909 });
910 }
911
912 @Override
913 public void updateField() {
914
915 theField.setValue(theItem.getValue());
916
917
918 theField.setTheAttributeState(TethysUIFieldAttribute.CHANGED, theItem.isChanged());
919 theField.adjustField();
920
921
922 final boolean isEnabled = !theItem.isHidden();
923 theField.setEnabled(isEnabled);
924 theButton.setEnabled(isEnabled);
925 }
926
927
928
929
930 private void handleDialog() {
931 ensureSelector();
932 theSelector.setInitialDirectory(new File(theItem.getValue()));
933 final File myDir = theSelector.selectDirectory();
934 if (myDir != null) {
935 theItem.setValue(myDir.getAbsolutePath());
936 notifyChanges();
937 }
938 }
939
940
941
942
943 private void ensureSelector() {
944 if (theSelector == null) {
945 theSelector = theGuiFactory.dialogFactory().newDirectorySelector();
946 theSelector.setTitle(theItem.getDisplay());
947 }
948 }
949 }
950 }