/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. */ package org.geogebra.common.euclidian.draw; import org.geogebra.common.awt.GColor; import org.geogebra.common.awt.GDimension; import org.geogebra.common.awt.GFont; import org.geogebra.common.awt.GGraphics2D; import org.geogebra.common.euclidian.BoundingBox; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.euclidian.EuclidianStatic; import org.geogebra.common.euclidian.EuclidianView; import org.geogebra.common.euclidian.RemoveNeeded; import org.geogebra.common.euclidian.event.FocusListener; import org.geogebra.common.euclidian.event.FocusListenerDelegate; import org.geogebra.common.euclidian.event.GFocusEvent; import org.geogebra.common.euclidian.event.KeyEvent; import org.geogebra.common.euclidian.event.KeyHandler; import org.geogebra.common.factories.AwtFactory; import org.geogebra.common.gui.inputfield.AutoCompleteTextField; import org.geogebra.common.javax.swing.GBox; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.geos.GeoAngle; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoInputBox; import org.geogebra.common.kernel.geos.GeoText; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.util.StringUtil; import org.geogebra.common.util.debug.Log; import org.geogebra.common.util.lang.Unicode; //import javax.swing.SwingUtilities; /** * Checkbox for free GeoBoolean object. * * @author Michael */ public class DrawInputBox extends CanvasDrawable implements RemoveNeeded { // TODO: examine these two, why are they needed and why these values. private static final double TF_HEIGHT_FACTOR = 1.22; /** ratio of length and screen width */ public static final double TF_WIDTH_FACTOR = 0.81; private static final int TF_MARGIN = 10; /** textfield */ final GeoInputBox geoInputBox; private boolean isVisible; private String oldCaption; /** textfield component */ // ButtonListener bl; private InputFieldListener ifListener; private KeyHandler ifKeyListener; /** * @param view * view * @param geo * textfield */ public DrawInputBox(EuclidianView view, GeoInputBox geo) { this.view = view; this.geoInputBox = geo; this.geo = geo; // action listener for checkBox // bl = new ButtonListener(); ifListener = new InputFieldListener(); ifKeyListener = new InputFieldKeyListener(); // ((geogebra.gui.inputfield.AutoCompleteTextField) // textField).addFocusListener(bl); if (getTextField() != null) { getTextField().addFocusListener( AwtFactory.getPrototype().newFocusListener(ifListener)); // label.addMouseListener(bl); // label.addMouseMotionListener(bl); getTextField().addKeyHandler(ifKeyListener); } update(); } /** * @return the text field */ GeoElement getGeo() { return geo; } /** * Listens to events in this textfield * * @author Michael + Judit */ public class InputFieldListener extends FocusListener implements FocusListenerDelegate { private String initialText; /** * @param e * focus event */ public void focusGained(GFocusEvent e) { if (!isSelectedForInput()) { return; } getView().getEuclidianController().textfieldHasFocus(true); geoInputBox.updateText(getTextField()); initialText = getTextField().getText(); } /** * @param e * focus event */ public void focusLost(GFocusEvent e) { if (!isSelectedForInput()) { return; } getView().getEuclidianController().textfieldHasFocus(false); // GGB-22 revert r43455 // stops alpha popup working hideWidget(); // make sure (expensive) update doesn't happen unless needed // also caused problems when Object Properties opened if (!getTextField().getText().equals(initialText)) { geoInputBox.textObjectUpdated(getTextField()); geoInputBox.textSubmitted(); draw(getView().getGraphicsForPen()); } } } /** * Listens to key events in this textfield * * @author Michael + Judit */ public class InputFieldKeyListener implements KeyHandler { /** * Handles new character * * @param e * key event */ @Override public void keyReleased(KeyEvent e) { if (!isSelectedForInput()) { Log.debug("[DF] keyReleased NOT " + "for " + labelDesc); return; } Log.debug("[DF] keyReleased for " + labelDesc); AutoCompleteTextField tf = getTextField(); if (e.isEnterKey()) { // Force focus removal in IE tf.setFocus(false); getView().requestFocusInWindow(); tf.setVisible(false); draw(getView().getGraphicsForPen()); geoInputBox.setText(tf.getText()); } else { GeoElementND linkedGeo = ((GeoInputBox) getGeo()) .getLinkedGeo(); if (linkedGeo instanceof GeoAngle) { String text = tf.getText(); // return if text already contains degree symbol or variable for (int i = 0; i < text.length(); i++) { if (!StringUtil.isDigit(text.charAt(i))) { return; } } int caretPos = tf.getCaretPosition(); // add degree symbol to end if it's (a) a GeoText and (b) // just digits tf.setText(text + Unicode.DEGREE); tf.setCaretPosition(caretPos); } } } } /** * @return whether this drawable is connected to the active input for the * view */ boolean isSelectedForInput() { return view.getTextField() != null && view.getTextField().getInputBox() == geoInputBox; } private int oldLength = 0; private GFont textFont; @Override final public void update() { update(false); } @Override final public void updateForView() { update(true); } private void update(boolean forView) { isVisible = geo.isEuclidianVisible(); if (getTextField() == null) { return; } if (!forView) { getTextField().setVisible(false); getBox().setVisible(false); } int length = geoInputBox.getLength(); if (length != oldLength) { getTextField().setColumns(length); getTextField().prepareShowSymbolButton( length > EuclidianConstants.SHOW_SYMBOLBUTTON_MINLENGTH); oldLength = length; } if (!isVisible) { return; } // show hide label by setting text if (geo.isLabelVisible()) { // get caption to show r String caption = geo.getCaption(StringTemplate.defaultTemplate); if (!caption.equals(oldCaption)) { oldCaption = caption; labelDesc = caption;// GeoElement.indicesToHTML(caption, true); } } setLabelFontSize((int) (view.getFontSize() * geoInputBox.getFontSizeMultiplier())); updateGeoIntputBox(); updateStyle(getTextField()); getBox().revalidate(); xLabel = geo.labelOffsetX; yLabel = geo.labelOffsetY; labelRectangle.setBounds(xLabel, yLabel, getPreferredSize().getWidth(), getPreferredSize().getHeight()); getBox().setBounds(labelRectangle); } private void updateGeoIntputBox() { geoInputBox.updateText(getTextField()); } private void updateStyle(AutoCompleteTextField tf) { GFont vFont = view.getFont(); textFont = view.getApplication().getFontCanDisplay(tf.getText(), false, vFont.getStyle(), getLabelFontSize()); tf.setOpaque(true); tf.setFont(textFont); GColor fgCol = GColor.BLACK; GColor bgCol = view.getBackgroundCommon(); if (geo != null) { fgCol = geo.getObjectColor(); if (geo.getBackgroundColor() != null) { bgCol = geo.getBackgroundColor(); } } tf.setForeground(fgCol); tf.setBackground(bgCol); tf.setFocusable(true); tf.setEditable(true); } /** * @return size of label + input */ public GDimension getTotalSize() { measureLabel(view.getGraphicsForPen(), geoInputBox, labelDesc); return new GDimension() { @Override public int getWidth() { return labelSize.x + getPreferredSize().getWidth(); } @Override public int getHeight() { return Math.max(labelSize.y, getPreferredSize().getHeight()); } }; } @Override public GDimension getPreferredSize() { // TODO: eliminate magic numbers return new GDimension() { @Override public int getWidth() { return (int) Math .round(((getView().getApplication().getFontSize() * geoInputBox.getFontSizeMultiplier())) * geoInputBox.getLength() * TF_WIDTH_FACTOR); } @Override public int getHeight() { return (int) Math .round(((getView().getApplication().getFontSize() * geoInputBox.getFontSizeMultiplier())) * TF_HEIGHT_FACTOR) + TF_MARGIN; } }; } @Override final public void draw(GGraphics2D g2) { if (isVisible) { drawOnCanvas(g2, geoInputBox.getText()); } } @Override protected void drawWidget(GGraphics2D g2) { GFont font = g2.getFont(); g2.setFont(getLabelFont().deriveFont(GFont.PLAIN)); boolean latexLabel = measureLabel(g2, geoInputBox, labelDesc); int textLeft = boxLeft + 2; int textBottom = boxTop + getTextBottom(); // TF Bounds labelRectangle.setBounds(boxLeft - 1, boxTop - 1, boxWidth, boxHeight - 3); if (isSelectedForInput()) { getBox().setBounds(labelRectangle); } GColor bgColor = geo.getBackgroundColor() != null ? geo.getBackgroundColor() : view.getBackgroundCommon(); getTextField().drawBounds(g2, bgColor, boxLeft, boxTop, boxWidth, boxHeight); highlightLabel(g2, latexLabel); g2.setPaint(geo.getObjectColor()); if (geo.isLabelVisible()) { drawLabel(g2, geoInputBox, labelDesc); } String text = geoInputBox.getText(); g2.setFont(textFont.deriveFont(GFont.PLAIN)); EuclidianStatic.drawIndexedString(view.getApplication(), g2, text.substring(0, getTruncIndex(text, g2)), textLeft, textBottom, false); g2.setFont(font); if (isSelectedForInput()) { getBox().repaint(g2); } } private int getTruncIndex(String text, GGraphics2D g2) { int idx = text.length(); int mt = measureTextWidth(text, g2.getFont(), g2); while (mt > boxWidth && idx > 0) { idx--; mt = measureTextWidth(text.substring(0, idx), g2.getFont(), g2); } return idx; } /** * Removes button from view again */ @Override final public void remove() { if (!isSelectedForInput()) { return; } // view.remove(getBox()); } /** * @param str * input string */ public void setFocus(final String str) { Log.debug("[DrawTextFied] setFocus: " + str); if (str != null && !"\t".equals(str)) { getTextField().wrapSetText(str); } getTextField().requestFocus(); } private void updateBoxPosition() { if (!isSelectedForInput()) { return; } getBox().revalidate(); measureLabel(view.getGraphicsForPen(), geoInputBox, labelDesc); labelRectangle.setBounds(boxLeft, boxTop, getPreferredSize().getWidth(), getPreferredSize().getHeight()); getBox().setBounds(labelRectangle); } @Override protected void showWidget() { GeoElement last = view.getTextField().getDrawTextField() .getGeoElement(); if (last != null) { Log.debug("LAST: " + last); } view.cancelBlur(); getBox().revalidate(); getBox().setVisible(true); attachTextField(); if (!view.getEuclidianController().isTemporaryMode()) { getTextField().requestFocus(); } } /** * Get view's textfield and attach to this */ public void attachTextField() { updateBoxPosition(); AutoCompleteTextField tf = getTextField(); tf.setDrawTextField(this); tf.setUsedForInputBox(geoInputBox); tf.setVisible(true); tf.setColumns(geoInputBox.getLength()); tf.setText(geoInputBox.getText()); setLabelFontSize((int) (view.getFontSize() * geoInputBox.getFontSizeMultiplier())); updateStyle(tf); tf.showPopupSymbolButton(false); if (geoInputBox .getLength() < EuclidianConstants.SHOW_SYMBOLBUTTON_MINLENGTH || geoInputBox.getLinkedGeo() instanceof GeoText) { tf.prepareShowSymbolButton(false); } else { tf.prepareShowSymbolButton(true); } } @Override protected void hideWidget() { if (!isSelectedForInput()) { return; } getTextField().hideDeferred(getBox()); } /** * Hide the widget */ public void removeTextField() { hideWidget(); } /** * @return textfield */ public AutoCompleteTextField getTextField() { AutoCompleteTextField tf = view.getTextField(geoInputBox, this); return tf; } private GBox getBox() { return view.getBoxForTextField(); } /** * Writes the real textfield's value to the GeoInputBox. */ public void apply() { geoInputBox.setText(getTextField().getText()); } @Override public BoundingBox getBoundingBox() { // TODO Auto-generated method stub return null; } @Override public void updateBoundingBox() { // TODO Auto-generated method stub } }