package org.geogebra.web.web.gui.view.algebra; import org.geogebra.common.euclidian.event.KeyEvent; import org.geogebra.common.euclidian.event.KeyHandler; import org.geogebra.common.gui.SetLabels; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.arithmetic.NumberValue; import org.geogebra.common.kernel.geos.GeoNumeric; import org.geogebra.common.main.App; import org.geogebra.common.main.GWTKeycodes; import org.geogebra.common.util.lang.Unicode; import org.geogebra.web.html5.gui.inputfield.AutoCompleteTextFieldW; import org.geogebra.web.html5.gui.util.AdvancedFlowPanel; import org.geogebra.web.web.gui.view.algebra.SliderTreeItemInterface.CancelListener; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseEvent; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.user.client.ui.Label; /** * Min/max setting for slider * */ public class MinMaxPanel extends AdvancedFlowPanel implements SetLabels, KeyHandler, MouseDownHandler, MouseUpHandler, CancelListener { private static volatile MinMaxPanel openedMinMaxPanel = null; /** * @return current panel */ public static MinMaxPanel getOpenedPanel() { return openedMinMaxPanel; } /** * Closes min/max/step settings panel of the slider and restores its size if * needed. */ public static void closeMinMaxPanel() { closeMinMaxPanel(true); } /** * Closes min/max/step settings panel of the slider. * * @param restore * Decides if the item size should be restored (AV was too arrow * to fit min/max panel) or not. */ public static void closeMinMaxPanel(boolean restore) { if (openedMinMaxPanel == null) { return; } openedMinMaxPanel.hide(restore); openedMinMaxPanel = null; } /** * Sets the currently open min/max panel of AV. * * @param panel * current panel */ public static void setOpenedMinMaxPanel(MinMaxPanel panel) { openedMinMaxPanel = panel; } /** * Input field for MinMaxPanel */ static class AVField extends AutoCompleteTextFieldW { private CancelListener listener; /** * @param columns * field width * @param app * application * @param listener * called on edit cancel */ public AVField(int columns, App app, CancelListener listener) { super(columns, app); this.listener = listener; setDeferredFocus(true); enableGGBKeyboard(); } @Override public void onKeyPress(KeyPressEvent e) { e.stopPropagation(); } @Override public void onKeyDown(KeyDownEvent e) { e.stopPropagation(); if (e.getNativeKeyCode() == GWTKeycodes.KEY_ESCAPE) { listener.cancel(); } } @Override public void onKeyUp(KeyUpEvent e) { e.stopPropagation(); } @Override protected void fieldFocus() { super.fieldFocus(); selectAll(); } } private SliderTreeItemInterface sliderTreeItem; /** min width of the panel */ public static final int MINMAX_MIN_WIDHT = 326; private AVField tfMin; private AVField tfMax; private AVField tfStep; private Label lblValue; private Label lblStep; private GeoNumeric num; private boolean keepOpen = false; private boolean focusRequested = false; private Kernel kernel; private App app; /** * @param item * parent tree item */ public MinMaxPanel(SliderTreeItemInterface item) { this.sliderTreeItem = item; num = (GeoNumeric) this.sliderTreeItem.getGeo(); kernel = num.getKernel(); app = kernel.getApplication(); tfMin = new AVField(4, app, this); tfMax = new AVField(4, app, this); tfStep = new AVField(4, app, this); lblValue = new Label(Unicode.LESS_EQUAL + " " + num .getCaption(StringTemplate.defaultTemplate) + " " + Unicode.LESS_EQUAL); // content set in update()->setLabels() lblStep = new Label(); addStyleName("minMaxPanel"); add(tfMin); add(lblValue); add(tfMax); add(lblStep); add(tfStep); tfMin.setDeferredFocus(true); tfMax.setDeferredFocus(true); tfStep.setDeferredFocus(true); tfMin.addKeyHandler(this); tfMax.addKeyHandler(this); tfStep.addKeyHandler(this); tfStep.addFocusHandler(new FocusHandler() { @Override public void onFocus(FocusEvent event) { stepFocused(event); } }); addMouseDownHandler(this); addMouseUpHandler(this); addBlurHandler(new BlurHandler() { @Override public void onBlur(BlurEvent event) { hide(); } }); update(); } /** * @param event * focus event in step field */ protected void stepFocused(FocusEvent event) { if (focusRequested) { event.preventDefault(); event.stopPropagation(); return; } tfStep.selectAll(); } /** * Update UIfrom geo */ public void update() { tfMin.setText(kernel.format(num.getIntervalMin(), StringTemplate.editTemplate)); tfMax.setText(kernel.format(num.getIntervalMax(), StringTemplate.editTemplate)); tfStep.setText( num.isAutoStep() ? "" : kernel.format( num.getAnimationStep(), StringTemplate.editTemplate)); setLabels(); } @Override public void setLabels() { lblStep.setText(app.getLocalization().getMenu("Step")); } /** * Show the panel */ public void show() { num.setAnimating(false); this.sliderTreeItem.expandSize(MINMAX_MIN_WIDHT); this.sliderTreeItem.setSliderVisible(false); setVisible(true); setKeepOpen(true); setOpenedMinMaxPanel(this); this.sliderTreeItem.setAnimPanelVisible(false); } /** * @param restore * whether to restore size */ private void hide(boolean restore) { if (restore) { this.sliderTreeItem.restoreSize(); } hide(); } /** * Hide the panel */ public void hide() { this.sliderTreeItem.setSliderVisible(true); this.sliderTreeItem.deferredResize(); setVisible(false); this.sliderTreeItem.setAnimPanelVisible(true); } @Override public void keyReleased(KeyEvent e) { if (e.isEnterKey()) { apply(); } } private void apply() { NumberValue min = getNumberFromInput(tfMin.getText().trim()); NumberValue max = getNumberFromInput(tfMax.getText().trim()); String stepText = tfStep.getText().trim(); if (min != null && max != null && min.getDouble() <= max.getDouble()) { num.setIntervalMin(min); num.setIntervalMax(max); if (stepText.isEmpty()) { num.setAutoStep(true); } else { num.setAutoStep(false); num.setAnimationStep(getNumberFromInput(stepText)); } num.update(); hide(true); } } // TODO: refactor needed: copied from SliderPanelW; private NumberValue getNumberFromInput(final String inputText) { boolean emptyString = "".equals(inputText); NumberValue value = null;// new MyDouble(kernel, Double.NaN); if (!emptyString) { value = kernel.getAlgebraProcessor() .evaluateToNumeric( inputText, false); } return value; } @Override public void cancel() { hide(); } @Override public void onMouseUp(MouseUpEvent event) { if (isKeepOpen()) { setKeepOpen(false); return; } if (focusRequested) { focusRequested = false; return; } event.stopPropagation(); if (!(selectAllOnFocus(tfMin, event) || selectAllOnFocus(tfMax, event) || selectAllOnFocus(tfStep, event))) { apply(); } } @Override public void onMouseDown(MouseDownEvent event) { if (event.getNativeButton() == NativeEvent.BUTTON_RIGHT) { return; } event.stopPropagation(); selectAllOnFocus(tfMin, event); selectAllOnFocus(tfMax, event); selectAllOnFocus(tfStep, event); } private static boolean selectAllOnFocus(AVField avField, MouseEvent<?> event) { if (RadioTreeItemController.isWidgetHit(avField, event)) { avField.removeDummyCursor(); avField.selectAll(); avField.addDummyCursor(); return true; } return false; } private boolean isKeepOpen() { return keepOpen; } private void setKeepOpen(boolean keepOpen) { this.keepOpen = keepOpen; } /** * Focus min field */ public void setMinFocus() { tfMin.requestFocus(); focusRequested = true; } /** * Focus max field */ public void setMaxFocus() { tfMax.requestFocus(); focusRequested = true; } }