package org.ovirt.engine.ui.common.widget.editor; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Objects; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.LabelElement; import com.google.gwt.dom.client.Style.Position; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.text.shared.Renderer; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.TakesValue; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HasConstrainedValue; import com.google.gwt.user.client.ui.RadioButton; public class RadioGroup<K> extends Composite implements TakesValue<K>, HasConstrainedValue<K> { private final Map<K, RadioButton> buttons = new HashMap<>(); private final Map<K, FlowPanel> panels = new HashMap<>(); private final FlowPanel wrapperPanel = new FlowPanel(); private final Renderer<K> renderer; private boolean enabled; private K selectedValue; private K oldSelectedValue; private final String groupString; private boolean usePatternfly = false; int tabIndex; char accessKey = 0; public RadioGroup(Renderer<K> renderer) { this.renderer = renderer; initWidget(wrapperPanel); groupString = getElement().getString(); } public int getTabIndex() { return tabIndex; } public void setAccessKey(char key) { this.accessKey = key; } public void setFocus(boolean focused) { RadioButton selectedButton = buttons.get(selectedValue); if (selectedButton != null) { selectedButton.setFocus(focused); } } protected LabelElement getRadioButtonWidgetLabel(RadioButton radioButton) { return LabelElement.as(Element.as(radioButton.getElement().getChild(1))); } public void setUsePatternFly(boolean use) { usePatternfly = use; } @Override public K getValue() { return selectedValue; } @Override public void setValue(K value) { setValue(value, false); } @Override public void setValue(K value, boolean fireEvents) { if (value == selectedValue || (selectedValue != null && selectedValue.equals(value))) { return; } if (!buttons.containsKey(value)) { addValue(value); } oldSelectedValue = selectedValue; selectedValue = value; updateButtons(); if (fireEvents) { ValueChangeEvent.fire(this, value); } } public void setTabIndex(int index) { this.tabIndex = index; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; for (RadioButton radioButton : buttons.values()) { radioButton.setEnabled(enabled); } } @Override public void setAcceptableValues(Collection<K> values) { buttons.clear(); panels.clear(); wrapperPanel.clear(); if (values != null) { for (final K value : values) { addValue(value); } } updateButtons(); } private void updateButtons() { for (Map.Entry<K, RadioButton> entry : buttons.entrySet()) { RadioButton radioButton = entry.getValue(); if (usePatternfly) { // patternfly hacks Element.as(radioButton.getElement().getChild(0)).addClassName("cbe_checkbox_pfly_fix"); //$NON-NLS-1$ getRadioButtonWidgetLabel(radioButton).getStyle().setPaddingLeft(10, Unit.PX); getRadioButtonWidgetLabel(radioButton).getStyle().setPosition(Position.RELATIVE); } if (entry.getKey().equals(selectedValue)) { radioButton.setValue(true); radioButton.setTabIndex(tabIndex); if (accessKey != 0) { radioButton.setAccessKey(accessKey); } } else if (Objects.equals(oldSelectedValue, entry.getKey())) { radioButton.setTabIndex(-1); radioButton.getElement().removeAttribute("accessKey"); //$NON-NLS-1$ } } } private void addValue(final K value) { if (value == null) { throw new IllegalArgumentException("null value is not permited"); //$NON-NLS-1$ } if (buttons.containsKey(value)) { throw new IllegalArgumentException("Duplicate value: " + value); //$NON-NLS-1$ } RadioButton radioButton = new RadioButton(groupString, SafeHtmlUtils.fromTrustedString(renderer.render(value))) { @Override public void onBrowserEvent(Event event) { super.onBrowserEvent(event); boolean value = getValue(); // This hack is needed because there are cases RadioButton.oldValue is not properly updated // (e.g- when changing the radio button selection with the arrows buttons there is no event that // catches // that the RadioButton.oldValue should be changed to false), // and it causes the event not to be fired although it should be. if (Event.ONCLICK == DOM.eventGetType(event) && value && this != buttons.get(selectedValue)) { ValueChangeEvent.fire(this, value); } } }; radioButton.addValueChangeHandler(event -> { if (event.getValue()) { setValue(value, true); } }); radioButton.getElement().getStyle().setMarginRight(20, Unit.PX); buttons.put(value, radioButton); FlowPanel panel = new FlowPanel(); panels.put(value, panel); panel.add(radioButton); wrapperPanel.add(panel); } @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler<K> handler) { return addHandler(handler, ValueChangeEvent.getType()); } public FlowPanel getPanel(K key) { return panels.get(key); } public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) { return addDomHandler(handler, KeyUpEvent.getType()); } public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) { return addDomHandler(handler, KeyDownEvent.getType()); } public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) { return addDomHandler(handler, KeyPressEvent.getType()); } }