/* 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.desktop.gui.dialog; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; import java.util.TreeSet; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.ScrollPaneConstants; import javax.swing.SwingConstants; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.UndoableEditEvent; import javax.swing.event.UndoableEditListener; import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.StyleConstants; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; import org.geogebra.common.euclidian.EuclidianConstants; import org.geogebra.common.euclidian.EuclidianViewInterfaceCommon; import org.geogebra.common.gui.InputHandler; import org.geogebra.common.gui.dialog.TextInputDialog; import org.geogebra.common.gui.util.SelectionTable; import org.geogebra.common.gui.util.TableSymbols; import org.geogebra.common.gui.util.TableSymbolsLaTeX; import org.geogebra.common.gui.view.algebra.DialogType; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.Matrix.Coords; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoText; import org.geogebra.common.kernel.kernelND.GeoElementND; import org.geogebra.common.kernel.kernelND.GeoPointND; import org.geogebra.common.main.App; import org.geogebra.common.main.GeoGebraColorConstants; import org.geogebra.common.main.MyError; import org.geogebra.common.main.error.ErrorHandler; import org.geogebra.common.plugin.EventType; import org.geogebra.common.util.AsyncOperation; import org.geogebra.common.util.debug.Log; import org.geogebra.desktop.awt.GColorD; import org.geogebra.desktop.gui.DynamicTextInputPane; import org.geogebra.desktop.gui.DynamicTextInputPane.DynamicTextField; import org.geogebra.desktop.gui.GuiManagerD; import org.geogebra.desktop.gui.util.GeoGebraIconD; import org.geogebra.desktop.gui.util.LatexTable; import org.geogebra.desktop.gui.util.PopupMenuButtonD; import org.geogebra.desktop.gui.util.SelectionTableD; import org.geogebra.desktop.gui.view.properties.PropertiesViewD; import org.geogebra.desktop.main.AppD; /** * Input dialog for GeoText objects with additional option to set a * "LaTeX formula" flag * * @author hohenwarter */ public class TextInputDialogD extends InputDialogD implements DocumentListener, TextInputDialog { // editor and preview panels private DynamicTextInputPane editor; private TextPreviewPanelD textPreviewer; // GUI private JCheckBox cbLaTeX; private JToolBar toolBar; private JPanel previewPanel, editPanel, toolPanel; private PopupMenuButtonD btInsertLaTeX, btInsertUnicode, btInsertGeo; private JLabel previewHeader, editHeader; // text handling fields private GeoText editGeo; private boolean isLaTeX; GeoPointND startPoint; boolean rw; private boolean isTextMode = false; // recent symbol fields private SelectionTableD recentSymbolTable; private ArrayList<String> recentSymbolList; // JList for the object menu popup private JList geoList; boolean isIniting; boolean isBtnInsertLatexLoaded = false; UndoManager undo = null; Document doc = null; // map to hold LatexButton menu titles private HashMap<String, JMenuItem> laTexButtonTitleMap; private boolean mayDetectLaTeX = true; /** * Input Dialog for a GeoText object * * @param app2 * @param title * @param editGeo * @param startPoint * @param cols * @param rows * @param isTextMode */ public TextInputDialogD(App app2, String title, GeoText editGeo, GeoPointND startPoint, boolean rw, int cols, int rows, boolean isTextMode) { super(((AppD) app2).getFrame(), false, ((AppD) app2).getLocalization()); this.app = (AppD) app2; this.startPoint = startPoint; this.rw = rw; this.isTextMode = isTextMode; this.editGeo = editGeo; setInputHandler(new TextInputHandler()); isIniting = true; // build input dialog GUI createGUI(title, "", false, cols, rows, false, false, false, false, DialogType.DynamicText); addHelpButton(App.WIKI_TEXT_TOOL); editor = (DynamicTextInputPane) inputPanel.getTextComponent(); textPreviewer = new TextPreviewPanelD(app2.getKernel()); // build the rest of the GUI createAdditionalGUI(); // init editor with GeoText text setGeoText(editGeo); updatePreviewText(); editor.getDocument().addDocumentListener(this); // add key listener to the editor editor.addKeyListener(new MyKeyListener()); wrappedDialog.setResizable(true); isIniting = false; setLabels(title); undo = new UndoManager(); doc = editor.getDocument(); doc.addUndoableEditListener(new UndoableEditListener() { @Override public void undoableEditHappened(UndoableEditEvent e) { undo.addEdit(e.getEdit()); } }); editor.getActionMap().put("Undo", new AbstractAction("Undo") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { try { if (undo.canUndo()) { undo.undo(); } } catch (CannotUndoException e1) { Log.debug("Cannot Undo"); } } }); editor.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "Undo"); editor.getActionMap().put("Redo", new AbstractAction("Redo") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { try { if (undo.canRedo()) { undo.redo(); } } catch (CannotRedoException e2) { Log.debug("Cannot Redo"); } } }); editor.getInputMap().put(KeyStroke.getKeyStroke("control Y"), "Redo"); wrappedDialog.pack(); } /* * // override because we don't want to set mode to another mode public void * windowGainedFocus(WindowEvent arg0) { if (!isModal()) { * app.setCurrentSelectionListener(null); } * app.getGuiManager().setCurrentTextfield(this, true); } */ @Override public void reInitEditor(GeoText text, GeoPointND startPoint1, boolean rw1) { this.startPoint = startPoint1; this.rw = rw1; setGeoText(text); isTextMode = app.getMode() == EuclidianConstants.MODE_TEXT; updatePreviewText(); editor.requestFocus(); } private void createAdditionalGUI() { showSymbolTablePopup(false); // create LaTeX checkbox cbLaTeX = new JCheckBox(); cbLaTeX.setSelected(isLaTeX); cbLaTeX.addActionListener(this); // create insertion buttons btInsertUnicode = new PopupMenuButtonD(app); btInsertLaTeX = new PopupMenuButtonD(app); btInsertLaTeX.setVisible(false); // (build the latex button on demand) // buildInsertLaTeXButton(); buildInsertUnicodeButton(); buildInsertGeoButton(); // build toolbar toolPanel = new JPanel(new BorderLayout()); toolBar = new JToolBar(); toolBar.add(cbLaTeX); toolBar.add(btInsertLaTeX); toolBar.add(Box.createRigidArea(new Dimension(5, 1))); toolBar.addSeparator(); toolBar.add(Box.createRigidArea(new Dimension(5, 1))); toolBar.add(btInsertUnicode); toolBar.add(Box.createRigidArea(new Dimension(5, 1))); toolBar.addSeparator(); toolBar.add(Box.createRigidArea(new Dimension(5, 1))); toolBar.add(btInsertGeo); toolBar.setFloatable(false); toolPanel.add(toolBar, BorderLayout.NORTH); toolPanel.add(createRecentSymbolTable(), BorderLayout.SOUTH); // create edit panel to contain both the input panel and toolbar editHeader = new JLabel(); editHeader.setBorder(BorderFactory.createEmptyBorder(2, 2, 0, 2)); editPanel = new JPanel(new BorderLayout(2, 2)); editPanel.add(editHeader, BorderLayout.NORTH); editPanel.add(inputPanel, BorderLayout.CENTER); editPanel.add(toolPanel, BorderLayout.SOUTH); editPanel.setBorder(BorderFactory.createEtchedBorder()); editPanel.setMinimumSize(new Dimension(200, 120)); // create preview panel previewHeader = new JLabel(); previewHeader.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); JPanel p = new JPanel(new BorderLayout()); p.add(textPreviewer.getJPanel(), BorderLayout.CENTER); previewPanel = new JPanel(new BorderLayout()); previewPanel.add(previewHeader, BorderLayout.NORTH); previewPanel.add(new JScrollPane(p), BorderLayout.CENTER); previewPanel.setMinimumSize(new Dimension(200, 50)); // set sizes Dimension d = inputPanel.getPreferredSize(); d.height = 60; // this.getFont().getSize()*10; inputPanel.setPreferredSize(d); previewPanel.setPreferredSize(inputPanel.getPreferredSize()); // textPreviewer.setPreferredSize(editor.getPreferredSize()); // put the preview and edit panels into a split pane JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, editPanel, previewPanel); previewPanel.setPreferredSize(editPanel.getPreferredSize()); sp.setResizeWeight(0.75); sp.setBorder(BorderFactory.createEmptyBorder()); // put all the sub-panels together JPanel centerPanel = new JPanel(new BorderLayout()); centerPanel.add(sp, BorderLayout.CENTER); centerPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); wrappedDialog.getContentPane().add(centerPanel, BorderLayout.CENTER); centerOnScreen(); } /** * Builds unicode insertion button and drop down tables. */ private void buildInsertUnicodeButton() { btInsertUnicode.removeAllMenuItems(); btInsertUnicode.setKeepVisible(false); btInsertUnicode.setStandardButton(true); btInsertUnicode.setFixedIcon(GeoGebraIconD.createDownTriangleIcon(10)); // btInsertUnicode.setText("Symbols"); String[][] map = TableSymbols.basicSymbolsMap(loc); JMenu menu = new JMenu(loc.getMenu("Properties.Basic")); menu.add(new LatexTable(app, this, btInsertUnicode, TableSymbols.basicSymbols(app.getLocalization(), map), -1, 11, SelectionTable.MODE_TEXT)); btInsertUnicode.addPopupMenuItem(menu); // btInsertUnicode.addPopupMenuItem(createMenuItem(SymbolTable.math_ops,0,1,2)); btInsertUnicode.addPopupMenuItem( createMenuItem(TableSymbols.operators, -1, 8)); btInsertUnicode.addPopupMenuItem( createMenuItem(TableSymbols.greekLettersPlusVariants(), -1, 8)); btInsertUnicode .addPopupMenuItem(createMenuItem(TableSymbols.analysis, -1, 8)); btInsertUnicode .addPopupMenuItem(createMenuItem(TableSymbols.sets, -1, 8)); btInsertUnicode .addPopupMenuItem(createMenuItem(TableSymbols.logical, -1, 8)); btInsertUnicode.addPopupMenuItem( createMenuItem(TableSymbols.sub_superscripts, -1, 10)); btInsertUnicode.addPopupMenuItem( createMenuItem(TableSymbols.basic_arrows, -1, 8)); btInsertUnicode.addPopupMenuItem( createMenuItem(TableSymbols.otherArrows, -1, 8)); btInsertUnicode.addPopupMenuItem( createMenuItem(TableSymbols.geometricShapes, -1, 8)); btInsertUnicode.addPopupMenuItem( createMenuItem(TableSymbols.games_music, -1, 7)); btInsertUnicode .addPopupMenuItem(createMenuItem(TableSymbols.currency, -1, 8)); btInsertUnicode.addPopupMenuItem( createMenuItem(TableSymbols.handPointers, -1, 6)); app.setComponentOrientation(menu); } /** * Creates a sub-menu for the unicode insert button. */ private JMenu createMenuItem(String[] table, int rows, int columns) { JMenu menu = new JMenu( table[0] + " " + table[1] + " " + table[2] + " "); menu.add(new LatexTable(app, this, btInsertUnicode, table, rows, columns, SelectionTable.MODE_TEXT)); return menu; } /** * Builds LaTeX insertion button and drop down tables. */ private void buildInsertLaTeXButton() { btInsertLaTeX.removeAllMenuItems(); btInsertLaTeX.setKeepVisible(false); btInsertLaTeX.setStandardButton(true); btInsertLaTeX.setFixedIcon(GeoGebraIconD.createDownTriangleIcon(10)); btInsertLaTeX.setVisible(false); laTexButtonTitleMap = new HashMap<String, JMenuItem>(); JMenu menu; menu = new JMenu(); laTexButtonTitleMap.put("RootsAndFractions", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.roots_fractions, 1, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("SumsAndIntegrals", menu); LatexTable table = new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.sums, 1, -1, SelectionTable.MODE_LATEX); // table.setCaretPosition(-3); menu.add(table); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("Brackets", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.brackets, 2, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("Accents", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.accents, 2, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("AccentsExt", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.accentsExtended, 2, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("Matrices", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.matrices, 1, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("FrakturLetters", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.mathfrak(), 4, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("CalligraphicLetters", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.mathcal(), 2, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("BlackboardLetters", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.mathbb(), 2, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); menu = new JMenu(); laTexButtonTitleMap.put("CursiveLetters", menu); menu.add(new LatexTable(app, this, btInsertLaTeX, TableSymbolsLaTeX.mathscr(), 2, -1, SelectionTable.MODE_LATEX)); btInsertLaTeX.addPopupMenuItem(menu); JMenuItem menuItem = new JMenuItem(); laTexButtonTitleMap.put("Space", menuItem); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { insertString(" \\; "); } }); btInsertLaTeX.addPopupMenuItem(menuItem); isBtnInsertLatexLoaded = true; updateInsertLaTeXButtonLabels(); } public void updateInsertLaTeXButtonLabels() { if (!isBtnInsertLatexLoaded) { return; } for (Entry<String, JMenuItem> entry : laTexButtonTitleMap.entrySet()) { String text = entry.getKey(); entry.getValue().setText(loc.getMenu(text)); } } /** * Creates an array of labels of existing geos that can be inserted into the * editor as dynamic text */ private String[] getGeoObjectList() { TreeSet<GeoElement> ts = app.getKernel().getConstruction() .getGeoSetLabelOrder(); ArrayList<String> list = new ArrayList<String>(); // first possibility : create empty box list.add(loc.getMenu("EmptyBox")); // add all geos Iterator<GeoElement> iter = ts.iterator(); while (iter.hasNext()) { GeoElement g = iter.next(); if (g.isLabelSet() && !g.equals(editGeo)) { list.add(g.getLabelSimple()); } } String[] geoArray = new String[list.size()]; geoArray = list.toArray(geoArray); return geoArray; } /** * Builds GeoElement insertion button. */ private void buildInsertGeoButton() { // create a JList to hold the geo labels for the object popup menu geoList = new JList(getGeoObjectList()); geoList.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4)); JScrollPane scroller = new JScrollPane(geoList); scroller.setBorder(BorderFactory.createEmptyBorder()); scroller.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); // add a list selection listener that will insert a selected geo into // the editor geoList.getSelectionModel() .addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { String label = (String) geoList.getSelectedValue(); if (label != null && e.getFirstIndex() == 0) { insertEmptyDynamicText(); } else { insertGeoElement( app.getKernel().lookupLabel(label)); } btInsertGeo.handlePopupActionEvent(); geoList.getSelectionModel().clearSelection(); } } }); // create a popup button and add the list to it btInsertGeo = new PopupMenuButtonD(app) { private static final long serialVersionUID = 1L; // update the object list before opening the popup @Override public boolean prepareToShowPopup() { geoList.setListData(getGeoObjectList()); int rowCount = Math.min(8, geoList.getModel().getSize()); geoList.setVisibleRowCount(rowCount); return geoList.getModel().getSize() > 0; } }; btInsertGeo.addPopupMenuItem(scroller); btInsertGeo.setKeepVisible(false); btInsertGeo.setStandardButton(true); btInsertGeo.setFixedIcon(GeoGebraIconD.createDownTriangleIcon(10)); } // ============================================================= // Recent symbol buttons // ============================================================= public JToolBar createRecentSymbolTable() { recentSymbolList = ((GuiManagerD) app.getGuiManager()) .getRecentSymbolList(); recentSymbolTable = new SelectionTableD(app, recentSymbolList.toArray(), 1, recentSymbolList.size(), new Dimension(24, 24), SelectionTable.MODE_TEXT); recentSymbolTable.setHorizontalAlignment(SwingConstants.CENTER); recentSymbolTable.setSelectedIndex(0); // this.setUseColorSwatchBorder(true); recentSymbolTable.setShowGrid(true); recentSymbolTable.setGridColor( GColorD.getAwtColor(GeoGebraColorConstants.TABLE_GRID_COLOR)); recentSymbolTable.setBorder(BorderFactory.createLoweredBevelBorder()); recentSymbolTable.setShowSelection(false); recentSymbolTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { insertString( recentSymbolList .get(recentSymbolTable.getSelectedIndex()), TextInputDialogD.this.isLaTeX); } }); JToolBar p = new JToolBar(); p.setFloatable(false); // p.add(new JLabel("Recent: ")); p.add(recentSymbolTable); p.setAlignmentX(Component.LEFT_ALIGNMENT); p.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5)); return p; } public void addRecentSymbol(String newSymbol) { if (!recentSymbolList.contains(newSymbol)) { this.recentSymbolList.add(0, newSymbol); this.recentSymbolList.remove(recentSymbolList.size() - 1); updateRecentSymbolTable(); } } public void updateRecentSymbolTable() { recentSymbolTable.populateModel(recentSymbolList.toArray()); } // ============================================================= // Getters/Setters // ============================================================= public void setLabels() { setLabels(loc.getMenu("Text")); } @Override public void setLabels(String title) { if (isIniting) { return; } super.setLabels(title); if (editHeader != null) { editHeader.setText(loc.getMenu("Edit")); } if (previewHeader != null) { previewHeader.setText(loc.getMenu("Preview")); } // rebuild the symbol tables to catch localized symbols buildInsertUnicodeButton(); updateInsertLaTeXButtonLabels(); if (cbLaTeX.isSelected()) { cbLaTeX.setText(null); } else { // add some spacing to compensate for replacement by wider // LateXButton cbLaTeX.setText(" " + loc.getMenu("LaTeXFormula") + " "); } btInsertLaTeX.setText(loc.getMenu("LaTeXFormula")); btInsertUnicode.setText(loc.getMenu("Symbols")); btInsertGeo.setText(loc.getMenu("Objects")); } /** * Sets the GeoText element to be edited. Also sets the editor content to * fit the new GeoText string and sets the LaTeX flag. * * @param geo * GeoText element to be edited */ public void setGeoText(GeoText geo) { handlingDocumentEventOff = true; this.editGeo = geo; boolean createText = geo == null; isLaTeX = geo == null ? false : geo.isLaTeX(); // TODO: not sure if this old code is needed anymore if (createText) { // initString = " \"\" "; setInitString(null); isLaTeX = false; } else { setInitString(""); if (geo.isIndependent()) { setInitString(geo.getTextString()); if (geo.getKernel().lookupLabel(getInitString()) != null) { setInitString("\"" + getInitString() + "\""); } } else { setInitString(geo.getDefinition(StringTemplate.defaultTemplate)); } isLaTeX = geo.isLaTeX(); } // ---------------------------------------------- editor.setText(geo, this); editor.setCaretPosition(0); cbLaTeX.setSelected(false); if (isLaTeX) { cbLaTeX.doClick(); } handlingDocumentEventOff = false; updatePreviewText(); editOccurred = false; } /** * @return toolbar with buttons for inserting text symbols and LaTeX * formulas */ public JPanel getToolBar() { return toolPanel; } /** * @return panel with textarea */ public JPanel getInputPanel() { return inputPanel; } /** * @return preview panel */ public JPanel getPreviewPanel() { return previewPanel; } /** * @return apply button */ public JButton getApplyButton() { return btApply; } /** * Returns state of LaTeX Formula checkbox. * * @return true if switched to LaTeX mode */ public boolean isLaTeX() { return cbLaTeX.isSelected(); } // ============================================================= // Event handlers // ============================================================= @Override public void actionPerformed(ActionEvent e) { Object source = e.getSource(); try { if (source == btOK || source == inputPanel.getTextComponent()) { isLaTeX = cbLaTeX.isSelected(); editOccurred = false; getInputHandler().processInput(editor.buildGeoGebraString(isLaTeX), this, new AsyncOperation<Boolean>() { @Override public void callback(Boolean finished) { editOccurred = false; if (wrappedDialog.isShowing()) { // text dialog window is used and open if (isTextMode) { // don't set mode setVisibleForTools(!finished); } else { setVisible(!finished); } if (isTextMode) { app.setMode( EuclidianConstants.MODE_TEXT); return; } } if (finished) { app.setMode(EuclidianConstants.MODE_MOVE); } } }); } else if (source == btCancel) { if (wrappedDialog.isShowing()) { setVisible(false); } else { setGeoText(editGeo); } if (isTextMode) { app.setMode(EuclidianConstants.MODE_TEXT); } } else if (source == cbLaTeX) { if (!isBtnInsertLatexLoaded) { buildInsertLaTeXButton(); isBtnInsertLatexLoaded = true; } btInsertLaTeX.setVisible(cbLaTeX.isSelected()); btInsertLaTeX.revalidate(); setLabels(); isLaTeX = cbLaTeX.isSelected(); this.mayDetectLaTeX = isLaTeX; updatePreviewText(); // use of $ deprecated (doesn't work in HTML5) // if (isLaTeX && inputPanel.getText().length() == 0) { // insertString("$ $"); // setRelativeCaretPosition(-2); // } editOccurred = true; } } catch (Exception ex) { // do nothing on uninitializedValue ex.printStackTrace(); } } private class MyKeyListener extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { if ((e.isControlDown() || AppD.isControlDown(e)) && e.getKeyCode() == KeyEvent.VK_SPACE) { if (isLaTeX) { inputPanel.insertString("\\:"); } } if ((e.isAltDown() || AppD.isAltDown(e))) { switch (e.getKeyCode()) { default: // do nothing break; case KeyEvent.VK_LEFT: Element elem; int i; for (i = editor.getCaretPosition() - 1; i >= 0; i--) { elem = editor.doc.getCharacterElement(i); // give focus to first dynamic text field if (elem.getName().equals("component")) { DynamicTextField tf = (DynamicTextField) StyleConstants .getComponent(elem.getAttributes()); tf.requestFocus(); tf.setCaretPosition(tf.getText().length()); break; } } // set caret: most left position before next component editor.setCaretPosition(i + 1); break; case KeyEvent.VK_RIGHT: for (i = editor.getCaretPosition(); i < editor.doc .getLength(); i++) { elem = editor.doc.getCharacterElement(i); // give focus to first dynamic text field if (elem.getName().equals("component")) { DynamicTextField tf = (DynamicTextField) StyleConstants .getComponent(elem.getAttributes()); tf.requestFocus(); tf.setCaretPosition(0); break; } } // set caret: most right position before next component editor.setCaretPosition(i); break; case KeyEvent.VK_ENTER: case KeyEvent.VK_UP: case KeyEvent.VK_DOWN: insertEmptyDynamicText(); break; } } } } /** * exit the text field, ie set the caret just before/after the text field * * @param tf * the text field * @param isLeft * before if true, after if false */ public void exitTextField(DynamicTextField tf, boolean isLeft) { Element elem; int i; for (i = 0; i < editor.doc.getLength(); i++) { elem = editor.doc.getCharacterElement(i); // find elem corresponding the text field if (elem.getName().equals("component")) { if (tf == (DynamicTextField) StyleConstants .getComponent(elem.getAttributes())) { break; } } } if (isLeft) { editor.setCaretPosition(i); // set caret: just before field } else { editor.setCaretPosition(i + 1); // set caret: just after field } editor.requestFocus(); } @Override public void updateFonts() { super.updateFonts(); Font font = app.getPlainFont(); if (isBtnInsertLatexLoaded) { buildInsertLaTeXButton(); btInsertLaTeX.setVisible(cbLaTeX.isSelected()); } btInsertLaTeX.setFont(font); btInsertUnicode.setFont(font); buildInsertUnicodeButton(); btInsertGeo.setFont(font); geoList.setFont(font); cbLaTeX.setFont(font); previewHeader.setFont(font); textPreviewer.updateFonts(); } // ============================================================= // Document listener // ============================================================= @Override public void changedUpdate(DocumentEvent e) { // do nothing } /** * Called when an insertion is made in the textarea * * @param e * the event */ @Override public void insertUpdate(DocumentEvent e) { handleDocumentEvent(); } /** * Called when a remove is made in the textarea * * @param e * the event */ @Override public void removeUpdate(DocumentEvent e) { handleDocumentEvent(); } /** * used for update to avoid several updates */ private boolean handlingDocumentEventOff = false; /** * false on init, become true when an edit occurs */ private boolean editOccurred = false; /** * * apply edit modifications */ public void applyModifications() { if (editOccurred) { editOccurred = false;// do this first to ensure no circular call getInputHandler().processInput(editor.buildGeoGebraString(isLaTeX), this, new AsyncOperation<Boolean>() { @Override public void callback(Boolean obj) { editOccurred = false; } }); } } public void handleDocumentEvent() { if (handlingDocumentEventOff) { return; } editOccurred = true; updatePreviewText(); } private void updatePreviewText() { boolean wasLaTeX = isLaTeX; isLaTeX = textPreviewer.updatePreviewText(editGeo, editor.buildGeoGebraString(isLaTeX), isLaTeX, mayDetectLaTeX); if (isLaTeX && !wasLaTeX) { if (editGeo != null) { editGeo.setLaTeX(true, false); } cbLaTeX.setSelected(true); } } @Override public void insertGeoElement(GeoElement geo1) { if (geo1 == null) { return; } insertDynamicText(geo1.getLabel(StringTemplate.defaultTemplate)); editor.requestFocus(); } /** * insert dynamic text with string s in it */ public void insertEmptyDynamicText() { DynamicTextField d = insertDynamicText(" "); d.requestFocus(); d.setCaretPosition(1); } /** * insert dynamic text with string s in it * * @param s * string in the dynamic text * @return dynamic text field */ public DynamicTextField insertDynamicText(String s) { DynamicTextField d = editor.insertDynamicText(s, this); d.getDocument().addDocumentListener(this); return d; } // ============================================================= // TextInputHandler // ============================================================= /** * Handles creating or redefining GeoText using the current editor string. * */ private class TextInputHandler implements InputHandler { private Kernel kernel; protected TextInputHandler() { kernel = app.getKernel(); } @Override public void processInput(String inputValue0, ErrorHandler handler, final AsyncOperation<Boolean> callback) { if (inputValue0 == null || (editGeo != null && editGeo.isProtected(EventType.UPDATE)) || (editGeo != null && !editGeo.isLabelSet())) { callback.callback(false); return; } String inputValue = inputValue0; // no quotes? if (inputValue.indexOf('"') < 0) { // this should become either // (1) a + "" where a is an object label or // (2) "text", a plain text // ad (1) OBJECT LABEL // add empty string to end to make sure // that this will become a text object if (kernel.lookupLabel(inputValue.trim()) != null) { inputValue = "(" + inputValue + ") + \"\""; } // ad (2) PLAIN TEXT // add quotes to string else { inputValue = "\"" + inputValue + "\""; } } else { // replace \n\" by \"\n, this is useful for e.g.: // "a = " + a + // "b = " + b inputValue = inputValue.replaceAll("\n\"", "\"\n"); } if ("\"\"".equals(inputValue)) { callback.callback(false); return; } // create new text boolean createText = editGeo == null; handler.resetError(); if (createText) { kernel.getAlgebraProcessor() .processAlgebraCommandNoExceptionHandling(inputValue, false, handler, true, getCallback(callback)); return; } // change existing text try { kernel.getAlgebraProcessor().changeGeoElement(editGeo, inputValue, true, true, TextInputDialogD.this, new AsyncOperation<GeoElementND>() { @Override public void callback(GeoElementND obj) { // update editGeo GeoText newText = (GeoText) obj; editGeo = newText; // make sure newText is using correct LaTeX // setting newText.setLaTeX(isLaTeX, true); if (newText.getParentAlgorithm() != null) { newText.getParentAlgorithm().update(); } else { newText.updateRepaint(); } app.doAfterRedefine(newText); callback.callback(obj != null); } }); // make redefined text selected // app.addSelectedGeo(newText); callback.callback(false); } catch (Exception e) { app.showError("ReplaceFailed"); callback.callback(false); } catch (MyError err) { app.showError(err); callback.callback(false); } } } @Override public void windowClosed(WindowEvent e) { app.setMoveMode(); } public AsyncOperation<GeoElementND[]> getCallback( final AsyncOperation<Boolean> callback) { // TODO Auto-generated method stub return new AsyncOperation<GeoElementND[]>() { @Override public void callback(GeoElementND[] ret) { if (ret != null && ret[0] instanceof GeoText) { Kernel kernel = ret[0].getKernel(); GeoText t = (GeoText) ret[0]; t.setLaTeX(isLaTeX, true); // make sure for new LaTeX texts we get nice "x"s if (isLaTeX) { t.setSerifFont(true); } EuclidianViewInterfaceCommon activeView = kernel .getApplication().getActiveEuclidianView(); if (startPoint.isLabelSet()) { t.checkVisibleIn3DViewNeeded(); try { t.setStartPoint(startPoint); } catch (Exception e) { // circular def: ignore } } else { // changed to RealWorld // not absolute // startpoint contains mouse coords // t.setAbsoluteScreenLoc(euclidianView.toScreenCoordX(startPoint.inhomX), // euclidianView.toScreenCoordY(startPoint.inhomY)); // t.setAbsoluteScreenLocActive(true); if (rw) { Coords coords = startPoint.getInhomCoordsInD3(); t.setRealWorldLoc( activeView.toRealWorldCoordX(coords.getX()), activeView .toRealWorldCoordY(coords.getY())); t.setAbsoluteScreenLocActive(false); } else { Coords coords = startPoint.getInhomCoordsInD3(); t.setAbsoluteScreenLoc((int) coords.getX(), (int) coords.getY()); t.setAbsoluteScreenLocActive(true); } // when not a point clicked, show text only in active // view if (activeView.isEuclidianView3D()) { // we need to add it to 3D view since by default // it may not kernel.getApplication().addToViews3D(t); app.removeFromEuclidianView(t); t.setVisibleInViewForPlane(false); kernel.getApplication().removeFromViewsForPlane(t); } else if (activeView.isDefault2D()) { if (kernel.getApplication() .isEuclidianView3Dinited()) { kernel.getApplication().removeFromViews3D(t); } else { t.removeViews3D(); } t.setVisibleInViewForPlane(false); kernel.getApplication().removeFromViewsForPlane(t); } else { // view for plane app.removeFromEuclidianView(t); if (kernel.getApplication() .isEuclidianView3Dinited()) { kernel.getApplication().removeFromViews3D(t); } else { t.removeViews3D(); } t.setVisibleInViewForPlane(true); kernel.getApplication().addToViewsForPlane(t); } } // make sure (only) the output of the text tool is selected activeView.getEuclidianController() .memorizeJustCreatedGeos(ret); t.updateRepaint(); app.storeUndoInfo(); callback.callback(true); return; } callback.callback(false); return; } }; } @Override public void handleDialogVisibilityChange(boolean isVisible) { if (!isVisible) { if (textPreviewer != null) { textPreviewer.removePreviewGeoText(); } // update the PropertiesView text editor if (app.getGuiManager().hasPropertiesView()) { ((PropertiesViewD) app.getGuiManager().getPropertiesView()) .updateTextEditor(editGeo); } } } public void reset() { this.editOccurred = false; } }