Revision a2415b9f
Added by Andreas Kohlbecker almost 6 years ago
src/main/java/eu/etaxonomy/cdm/vaadin/component/common/FilterableAnnotationsField.java | ||
---|---|---|
1 |
/** |
|
2 |
* Copyright (C) 2018 EDIT |
|
3 |
* European Distributed Institute of Taxonomy |
|
4 |
* http://www.e-taxonomy.eu |
|
5 |
* |
|
6 |
* The contents of this file are subject to the Mozilla Public License Version 1.1 |
|
7 |
* See LICENSE.TXT at the top of this package for the full license terms. |
|
8 |
*/ |
|
9 |
package eu.etaxonomy.cdm.vaadin.component.common; |
|
10 |
|
|
11 |
import java.util.ArrayList; |
|
12 |
import java.util.Arrays; |
|
13 |
import java.util.Collection; |
|
14 |
import java.util.List; |
|
15 |
|
|
16 |
import org.vaadin.viritin.FilterableListContainer; |
|
17 |
|
|
18 |
import com.vaadin.data.Container; |
|
19 |
import com.vaadin.data.Container.Filter; |
|
20 |
import com.vaadin.data.Item; |
|
21 |
import com.vaadin.data.Validator.InvalidValueException; |
|
22 |
import com.vaadin.data.fieldgroup.FieldGroup; |
|
23 |
import com.vaadin.data.util.BeanItemContainer; |
|
24 |
import com.vaadin.ui.Component; |
|
25 |
import com.vaadin.ui.CssLayout; |
|
26 |
import com.vaadin.ui.DefaultFieldFactory; |
|
27 |
import com.vaadin.ui.Field; |
|
28 |
import com.vaadin.ui.ListSelect; |
|
29 |
import com.vaadin.ui.Table; |
|
30 |
import com.vaadin.ui.Table.ColumnHeaderMode; |
|
31 |
import com.vaadin.ui.TextArea; |
|
32 |
|
|
33 |
import eu.etaxonomy.cdm.model.common.Annotation; |
|
34 |
import eu.etaxonomy.cdm.model.common.AnnotationType; |
|
35 |
import eu.etaxonomy.cdm.model.common.DefinedTermBase; |
|
36 |
import eu.etaxonomy.cdm.model.common.Language; |
|
37 |
import eu.etaxonomy.cdm.vaadin.util.converter.SetToListConverter; |
|
38 |
import eu.etaxonomy.cdm.vaadin.util.filter.CdmTermFilter; |
|
39 |
import eu.etaxonomy.vaadin.component.CompositeCustomField; |
|
40 |
|
|
41 |
/** |
|
42 |
* @author a.kohlbecker |
|
43 |
* @since Jun 20, 2018 |
|
44 |
* |
|
45 |
*/ |
|
46 |
public class FilterableAnnotationsField extends CompositeCustomField<List<Annotation>> { |
|
47 |
|
|
48 |
private static final long serialVersionUID = -8258550787601028813L; |
|
49 |
|
|
50 |
Class<List<Annotation>> type = (Class<List<Annotation>>)new ArrayList<Annotation>().getClass(); |
|
51 |
|
|
52 |
private CssLayout root = new CssLayout(); |
|
53 |
|
|
54 |
private Table table = new Table(); |
|
55 |
|
|
56 |
private List<AnnotationType> typesFilter = null; |
|
57 |
|
|
58 |
private Annotation emptyDefaultAnnotation = Annotation.NewInstance(null, Language.DEFAULT()); |
|
59 |
|
|
60 |
private BeanItemContainer<DefinedTermBase> typeSelectItemContainer; |
|
61 |
|
|
62 |
private FilterableListContainer<Annotation> container; |
|
63 |
|
|
64 |
public FilterableAnnotationsField() { |
|
65 |
this(null); |
|
66 |
} |
|
67 |
|
|
68 |
public FilterableAnnotationsField(String caption) { |
|
69 |
|
|
70 |
setCaption(caption); |
|
71 |
// annotations are always sets |
|
72 |
setConverter(new SetToListConverter<Annotation>()); |
|
73 |
|
|
74 |
root.setWidth(100, Unit.PERCENTAGE); |
|
75 |
|
|
76 |
// setup table |
|
77 |
table.setPageLength(1); |
|
78 |
table.setColumnHeaderMode(ColumnHeaderMode.HIDDEN); |
|
79 |
table.setWidth(100, Unit.PERCENTAGE); |
|
80 |
table.setTableFieldFactory(new DefaultFieldFactory() { |
|
81 |
|
|
82 |
private static final long serialVersionUID = 5437750882205859178L; |
|
83 |
|
|
84 |
@Override |
|
85 |
public Field<?> createField(Item item, Object propertyId, Component uiContext) { |
|
86 |
|
|
87 |
Field<?> field = createField(propertyId); |
|
88 |
if(field == null) { |
|
89 |
field = super.createField(item, propertyId, uiContext); |
|
90 |
} |
|
91 |
return field; |
|
92 |
|
|
93 |
} |
|
94 |
|
|
95 |
@Override |
|
96 |
public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) { |
|
97 |
|
|
98 |
Field<?> field = createField(propertyId); |
|
99 |
|
|
100 |
if(field == null) { |
|
101 |
field = super.createField(container, itemId, propertyId, uiContext); |
|
102 |
} |
|
103 |
return field; |
|
104 |
} |
|
105 |
|
|
106 |
protected Field<?> createField(Object propertyId) { |
|
107 |
Field<?> field = null; |
|
108 |
if(propertyId.equals("text")){ |
|
109 |
TextArea ta = new TextArea(); |
|
110 |
ta.setNullRepresentation(""); |
|
111 |
ta.setWidth(100, Unit.PERCENTAGE); |
|
112 |
field = ta; |
|
113 |
} else if(propertyId.equals("annotationType")) { |
|
114 |
ListSelect select = new ListSelect(); |
|
115 |
select.setContainerDataSource(typeSelectItemContainer); |
|
116 |
select.setWidth(100, Unit.PIXELS); |
|
117 |
select.setRows(1); |
|
118 |
field = select; |
|
119 |
} |
|
120 |
field.setStyleName(table.getStyleName()); |
|
121 |
return field; |
|
122 |
} |
|
123 |
}); |
|
124 |
|
|
125 |
addStyledComponent(table); |
|
126 |
|
|
127 |
} |
|
128 |
|
|
129 |
public void setAnnotationTypesVisible(AnnotationType ... types){ |
|
130 |
typesFilter = Arrays.asList(types); |
|
131 |
} |
|
132 |
|
|
133 |
/** |
|
134 |
* {@inheritDoc} |
|
135 |
*/ |
|
136 |
@Override |
|
137 |
protected void addDefaultStyles() { |
|
138 |
// no default styles here |
|
139 |
} |
|
140 |
|
|
141 |
/** |
|
142 |
* {@inheritDoc} |
|
143 |
*/ |
|
144 |
@Override |
|
145 |
public FieldGroup getFieldGroup() { |
|
146 |
// holds a Container instead // TODO can this cause a NPE? |
|
147 |
return null; |
|
148 |
} |
|
149 |
|
|
150 |
@Override |
|
151 |
public void commit() throws SourceException, InvalidValueException { |
|
152 |
table.commit(); |
|
153 |
Collection<Filter> filters = container.getContainerFilters(); |
|
154 |
super.commit(); |
|
155 |
for(Filter filter : filters){ |
|
156 |
container.addContainerFilter(filter); |
|
157 |
} |
|
158 |
System.err.println(container.size()); |
|
159 |
} |
|
160 |
|
|
161 |
|
|
162 |
|
|
163 |
/** |
|
164 |
* {@inheritDoc} |
|
165 |
*/ |
|
166 |
@Override |
|
167 |
protected List<Annotation> getInternalValue() { |
|
168 |
if(container == null || container.getItemIds() == null){ |
|
169 |
return null; |
|
170 |
} |
|
171 |
return new ArrayList<>(container.getItemIds()); |
|
172 |
} |
|
173 |
|
|
174 |
|
|
175 |
@Override |
|
176 |
protected void setInternalValue(List<Annotation> newValue) { |
|
177 |
boolean hasIncludeFilter = typesFilter != null && !typesFilter.isEmpty(); |
|
178 |
boolean onlyOneType = hasIncludeFilter && typesFilter.size() == 1; |
|
179 |
|
|
180 |
if(newValue.isEmpty()){ |
|
181 |
newValue.add(emptyDefaultAnnotation); |
|
182 |
if(onlyOneType){ |
|
183 |
emptyDefaultAnnotation.setAnnotationType(typesFilter.get(0)); |
|
184 |
} |
|
185 |
} |
|
186 |
container = new FilterableListContainer<Annotation>(newValue); |
|
187 |
if(hasIncludeFilter){ |
|
188 |
container.addContainerFilter(new CdmTermFilter<AnnotationType>("annotationType", typesFilter)); |
|
189 |
} |
|
190 |
table.setContainerDataSource(container); |
|
191 |
if(onlyOneType){ |
|
192 |
table.setVisibleColumns("text"); |
|
193 |
} else { |
|
194 |
table.setVisibleColumns("text", "annotationType"); |
|
195 |
} |
|
196 |
table.setEditable(true); |
|
197 |
} |
|
198 |
|
|
199 |
/** |
|
200 |
* {@inheritDoc} |
|
201 |
*/ |
|
202 |
@Override |
|
203 |
protected Component initContent() { |
|
204 |
root.addComponent(table); |
|
205 |
return root; |
|
206 |
} |
|
207 |
|
|
208 |
|
|
209 |
/** |
|
210 |
* {@inheritDoc} |
|
211 |
*/ |
|
212 |
@Override |
|
213 |
public Class<List<Annotation>> getType() { |
|
214 |
return type; |
|
215 |
} |
|
216 |
|
|
217 |
/** |
|
218 |
* @param buildTermItemContainer |
|
219 |
*/ |
|
220 |
public void setAnnotationTypeItemContainer(BeanItemContainer<DefinedTermBase> typeSelectItemContainer) { |
|
221 |
this.typeSelectItemContainer = typeSelectItemContainer; |
|
222 |
} |
|
223 |
|
|
224 |
|
|
225 |
} |
src/main/java/eu/etaxonomy/cdm/vaadin/model/name/TaxonNameDTO.java | ||
---|---|---|
8 | 8 |
*/ |
9 | 9 |
package eu.etaxonomy.cdm.vaadin.model.name; |
10 | 10 |
|
11 |
import java.util.ArrayList; |
|
11 | 12 |
import java.util.HashSet; |
12 | 13 |
import java.util.List; |
13 | 14 |
import java.util.Set; |
... | ... | |
65 | 66 |
return name.getAnnotations(); |
66 | 67 |
} |
67 | 68 |
|
69 |
public void setAnnotations(Set<Annotation> annotations) { |
|
70 |
List<Annotation> currentAnnotations = new ArrayList<>(name.getAnnotations()); |
|
71 |
List<Annotation> annotationsSeen = new ArrayList<>(); |
|
72 |
for(Annotation a : annotations){ |
|
73 |
if(a == null){ |
|
74 |
continue; |
|
75 |
} |
|
76 |
if(!currentAnnotations.contains(a)){ |
|
77 |
name.addAnnotation(a); |
|
78 |
} |
|
79 |
annotationsSeen.add(a); |
|
80 |
} |
|
81 |
for(Annotation a : currentAnnotations){ |
|
82 |
if(!annotationsSeen.contains(a)){ |
|
83 |
name.removeAnnotation(a); |
|
84 |
} |
|
85 |
} |
|
86 |
} |
|
87 |
|
|
68 | 88 |
public String getAppendedPhrase() { |
69 | 89 |
return name.getAppendedPhrase(); |
70 | 90 |
} |
src/main/java/eu/etaxonomy/cdm/vaadin/util/filter/CdmTermFilter.java | ||
---|---|---|
1 |
/** |
|
2 |
* Copyright (C) 2018 EDIT |
|
3 |
* European Distributed Institute of Taxonomy |
|
4 |
* http://www.e-taxonomy.eu |
|
5 |
* |
|
6 |
* The contents of this file are subject to the Mozilla Public License Version 1.1 |
|
7 |
* See LICENSE.TXT at the top of this package for the full license terms. |
|
8 |
*/ |
|
9 |
package eu.etaxonomy.cdm.vaadin.util.filter; |
|
10 |
|
|
11 |
import java.util.List; |
|
12 |
|
|
13 |
import com.vaadin.data.Container.Filter; |
|
14 |
import com.vaadin.data.Item; |
|
15 |
import com.vaadin.data.Property; |
|
16 |
|
|
17 |
import eu.etaxonomy.cdm.model.common.TermBase; |
|
18 |
|
|
19 |
/** |
|
20 |
* @author a.kohlbecker |
|
21 |
* @since Jun 21, 2018 |
|
22 |
* |
|
23 |
*/ |
|
24 |
public class CdmTermFilter<T extends TermBase> implements Filter { |
|
25 |
|
|
26 |
private static final long serialVersionUID = -613582375956129270L; |
|
27 |
|
|
28 |
private List<T> includeFilter; |
|
29 |
private Object propertyId; |
|
30 |
|
|
31 |
/** |
|
32 |
* |
|
33 |
* @param propertyId |
|
34 |
* @param includeFilter |
|
35 |
* @param includeNullValues true by default |
|
36 |
*/ |
|
37 |
public CdmTermFilter(Object propertyId, List<T> includeFilter){ |
|
38 |
this.propertyId = propertyId; |
|
39 |
this.includeFilter = includeFilter; |
|
40 |
} |
|
41 |
|
|
42 |
/** |
|
43 |
* {@inheritDoc} |
|
44 |
*/ |
|
45 |
@Override |
|
46 |
public boolean passesFilter(Object itemId, Item item) throws UnsupportedOperationException { |
|
47 |
Property property = item.getItemProperty(propertyId); |
|
48 |
Object value = property.getValue(); |
|
49 |
if(includeFilter.contains(value)){ |
|
50 |
return true; |
|
51 |
} |
|
52 |
return false; |
|
53 |
} |
|
54 |
|
|
55 |
@Override |
|
56 |
public boolean appliesToProperty(Object propertyId) { |
|
57 |
return this.propertyId.equals(propertyId); |
|
58 |
} |
|
59 |
|
|
60 |
} |
src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNameEditorPresenter.java | ||
---|---|---|
27 | 27 |
import eu.etaxonomy.cdm.model.agent.AgentBase; |
28 | 28 |
import eu.etaxonomy.cdm.model.agent.Person; |
29 | 29 |
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase; |
30 |
import eu.etaxonomy.cdm.model.common.AnnotationType; |
|
30 | 31 |
import eu.etaxonomy.cdm.model.common.CdmBase; |
31 | 32 |
import eu.etaxonomy.cdm.model.common.TermType; |
32 | 33 |
import eu.etaxonomy.cdm.model.name.Rank; |
... | ... | |
175 | 176 |
getView().getValidationField().getCitatonComboBox().getSelect().setCaptionGenerator(new CdmTitleCacheCaptionGenerator<Reference>()); |
176 | 177 |
getView().getValidationField().getCitatonComboBox().loadFrom(icbnCodesPagingProvider, icbnCodesPagingProvider, icbnCodesPagingProvider.getPageSize()); |
177 | 178 |
getView().getValidationField().getCitatonComboBox().getSelect().addValueChangeListener(new ToOneRelatedEntityReloader<>(getView().getValidationField().getCitatonComboBox(), this)); |
179 |
|
|
180 |
getView().getAnnotationsField().setAnnotationTypeItemContainer(selectFieldFactory.buildTermItemContainer( |
|
181 |
AnnotationType.EDITORIAL().getUuid(), AnnotationType.TECHNICAL().getUuid())); |
|
178 | 182 |
} |
179 | 183 |
|
180 | 184 |
/** |
... | ... | |
183 | 187 |
@Override |
184 | 188 |
protected TaxonName loadCdmEntity(UUID identifier) { |
185 | 189 |
|
186 |
List<String> initStrategy = Arrays.asList(new String []{ |
|
187 |
|
|
190 |
List<String> initStrategy = Arrays.asList( |
|
188 | 191 |
"$", |
192 |
"annotations.type", |
|
193 |
"annotations.*", // needed as log as we are using a table in FilterableAnnotationsField |
|
189 | 194 |
"rank.vocabulary", // needed for comparing ranks |
190 | 195 |
|
191 | 196 |
"nomenclaturalReference.authorship", |
... | ... | |
214 | 219 |
|
215 | 220 |
"relationsFromThisName", |
216 | 221 |
"homotypicalGroup.typifiedNames" |
217 |
|
|
218 |
} |
|
219 | 222 |
); |
220 | 223 |
|
221 | 224 |
TaxonName taxonName; |
... | ... | |
255 | 258 |
|
256 | 259 |
} |
257 | 260 |
} |
258 |
|
|
259 | 261 |
return taxonName; |
260 | 262 |
} |
261 | 263 |
|
src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditor.java | ||
---|---|---|
34 | 34 |
import com.vaadin.ui.TextField; |
35 | 35 |
|
36 | 36 |
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase; |
37 |
import eu.etaxonomy.cdm.model.common.AnnotationType; |
|
37 | 38 |
import eu.etaxonomy.cdm.model.common.CdmBase; |
38 | 39 |
import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction; |
39 | 40 |
import eu.etaxonomy.cdm.model.name.NameRelationshipType; |
... | ... | |
41 | 42 |
import eu.etaxonomy.cdm.model.name.TaxonName; |
42 | 43 |
import eu.etaxonomy.cdm.model.reference.Reference; |
43 | 44 |
import eu.etaxonomy.cdm.vaadin.component.TextFieldNFix; |
45 |
import eu.etaxonomy.cdm.vaadin.component.common.FilterableAnnotationsField; |
|
44 | 46 |
import eu.etaxonomy.cdm.vaadin.component.common.TeamOrPersonField; |
45 | 47 |
import eu.etaxonomy.cdm.vaadin.event.ReferenceEditorAction; |
46 | 48 |
import eu.etaxonomy.cdm.vaadin.event.TaxonNameEditorAction; |
... | ... | |
65 | 67 |
*/ |
66 | 68 |
@SpringComponent |
67 | 69 |
@Scope("prototype") |
68 |
public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO, TaxonName, TaxonNameEditorPresenter> implements TaxonNamePopupEditorView, AccessRestrictedView { |
|
70 |
public class TaxonNamePopupEditor extends AbstractCdmDTOPopupEditor<TaxonNameDTO, TaxonName, TaxonNameEditorPresenter> |
|
71 |
implements TaxonNamePopupEditorView, AccessRestrictedView { |
|
69 | 72 |
|
70 | 73 |
private static final long serialVersionUID = -7037436241474466359L; |
71 | 74 |
|
72 | 75 |
private final static int GRID_COLS = 4; |
73 | 76 |
|
74 |
private final static int GRID_ROWS = 16;
|
|
77 |
private final static int GRID_ROWS = 17;
|
|
75 | 78 |
|
76 | 79 |
private static final boolean HAS_BASIONYM_DEFAULT = false; |
77 | 80 |
|
... | ... | |
129 | 132 |
|
130 | 133 |
private ValueChangeListener updateFieldVisibilityListener = e -> updateFieldVisibility(); |
131 | 134 |
|
135 |
private FilterableAnnotationsField annotationsListField; |
|
136 |
|
|
137 |
private AnnotationType[] editableAnotationTypes = new AnnotationType[]{AnnotationType.EDITORIAL()}; |
|
138 |
|
|
139 |
|
|
140 |
/** |
|
141 |
* By default AnnotationType.EDITORIAL() is enabled. |
|
142 |
* |
|
143 |
* @return the editableAnotationTypes |
|
144 |
*/ |
|
145 |
public AnnotationType[] getEditableAnotationTypes() { |
|
146 |
return editableAnotationTypes; |
|
147 |
} |
|
148 |
|
|
149 |
/** |
|
150 |
* By default AnnotationType.EDITORIAL() is enabled. |
|
151 |
* |
|
152 |
* |
|
153 |
* @param editableAnotationTypes the editableAnotationTypes to set |
|
154 |
*/ |
|
155 |
public void setEditableAnotationTypes(AnnotationType ... editableAnotationTypes) { |
|
156 |
this.editableAnotationTypes = editableAnotationTypes; |
|
157 |
} |
|
158 |
|
|
132 | 159 |
/** |
133 | 160 |
* @param layout |
134 | 161 |
* @param dtoType |
... | ... | |
146 | 173 |
} |
147 | 174 |
|
148 | 175 |
|
149 |
|
|
150 | 176 |
/** |
151 | 177 |
* {@inheritDoc} |
152 | 178 |
*/ |
... | ... | |
420 | 446 |
exCombinationAuthorshipField.setWidth(100, Unit.PERCENTAGE); |
421 | 447 |
addField(exCombinationAuthorshipField, "exCombinationAuthorship", 0, row, GRID_COLS-1, row); |
422 | 448 |
|
449 |
row++; |
|
450 |
annotationsListField = new FilterableAnnotationsField("Editorial notes"); |
|
451 |
annotationsListField.setWidth(100, Unit.PERCENTAGE); |
|
452 |
annotationsListField.setAnnotationTypesVisible(editableAnotationTypes); |
|
453 |
addField(annotationsListField, "annotations", 0, row, GRID_COLS-1, row); |
|
454 |
|
|
423 | 455 |
// ----------------------------------------------------------------------------- |
424 | 456 |
|
425 | 457 |
setAdvancedModeEnabled(true); |
... | ... | |
831 | 863 |
return basionymToggle; |
832 | 864 |
} |
833 | 865 |
|
866 |
@Override |
|
867 |
public FilterableAnnotationsField getAnnotationsField() { |
|
868 |
return annotationsListField; |
|
869 |
} |
|
870 |
|
|
834 | 871 |
/** |
835 | 872 |
* {@inheritDoc} |
836 | 873 |
*/ |
... | ... | |
852 | 889 |
} |
853 | 890 |
} |
854 | 891 |
|
892 |
|
|
893 |
|
|
855 | 894 |
/** |
856 | 895 |
* @return the infraGenericEpithetField |
857 | 896 |
*/ |
src/main/java/eu/etaxonomy/cdm/vaadin/view/name/TaxonNamePopupEditorView.java | ||
---|---|---|
16 | 16 |
|
17 | 17 |
import eu.etaxonomy.cdm.model.name.TaxonName; |
18 | 18 |
import eu.etaxonomy.cdm.model.reference.Reference; |
19 |
import eu.etaxonomy.cdm.vaadin.component.common.FilterableAnnotationsField; |
|
19 | 20 |
import eu.etaxonomy.cdm.vaadin.component.common.TeamOrPersonField; |
20 | 21 |
import eu.etaxonomy.vaadin.component.NameRelationField; |
21 | 22 |
import eu.etaxonomy.vaadin.component.ToManyRelatedEntitiesComboboxSelect; |
... | ... | |
115 | 116 |
*/ |
116 | 117 |
public AbstractField<String> getInfraSpecificEpithetField(); |
117 | 118 |
|
119 |
/** |
|
120 |
* @return |
|
121 |
*/ |
|
122 |
FilterableAnnotationsField getAnnotationsField(); |
|
123 |
|
|
118 | 124 |
} |
src/main/java/eu/etaxonomy/vaadin/component/CompositeCustomField.java | ||
---|---|---|
29 | 29 |
/** |
30 | 30 |
* TODO implement height methods for full component size support |
31 | 31 |
* |
32 |
* Implementations need to override {@link AbstractField#setInternalValue(TeamOrPersonBase<?> newValue)} in order to |
|
33 |
* to set the item datasource of the fieldGroup for example: |
|
34 |
* <pre> |
|
35 |
* @Override |
|
36 |
protected void setInternalValue(TeamOrPersonBase<?> newValue) { |
|
37 |
... |
|
38 |
fieldGroup.setItemDataSource(new BeanItem<Team>((Team)newValue)); |
|
39 |
... |
|
40 |
} |
|
41 |
* </pre> |
|
42 |
* |
|
32 | 43 |
* @author a.kohlbecker |
33 | 44 |
* @since May 12, 2017 |
34 | 45 |
* |
src/main/java/eu/etaxonomy/vaadin/component/NestedFieldGroup.java | ||
---|---|---|
20 | 20 |
/** |
21 | 21 |
* Implementations return the local fieldGroup |
22 | 22 |
* |
23 |
* @return |
|
23 |
* @return the FieldGroup, may be <code>null</code>.
|
|
24 | 24 |
*/ |
25 | 25 |
public abstract FieldGroup getFieldGroup(); |
26 | 26 |
|
Also available in: Unified diff
ref #7458 initial implementation of a note field at the example of the TaxonNameEditor