package org.geogebra.desktop.cas.view; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.DefaultListSelectionModel; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.geogebra.common.cas.view.CASInputHandler; import org.geogebra.common.cas.view.CASTable; import org.geogebra.common.cas.view.CASView; import org.geogebra.common.gui.SetOrientation; import org.geogebra.common.kernel.geos.GeoCasCell; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.main.GeoGebraColorConstants; import org.geogebra.common.util.debug.Log; import org.geogebra.desktop.awt.GColorD; import org.geogebra.desktop.gui.GuiManagerD; import org.geogebra.desktop.gui.view.Gridable; import org.geogebra.desktop.main.AppD; import org.geogebra.desktop.util.CASDragGestureListener; import org.geogebra.desktop.util.CASDropTargetListener; /** * Computer algebra view. * * @author Markus Hohenwarter, Quan Yuan */ public class CASViewD extends CASView implements Gridable, SetOrientation { private JComponent component; private CASTableD consoleTable; private CASSubDialogD subDialog; private ListSelectionModel listSelModel; final private AppD app; final private RowHeaderD rowHeader; /** stylebar */ CASStyleBar styleBar; /** * Component representing this view */ static class CASComponent extends JComponent { private static final long serialVersionUID = 1L; } /** * Creates new CAS view * * @param app * application */ public CASViewD(final AppD app) { super(app.getKernel()); component = new CASComponent(); this.app = app; listSelModel = new DefaultListSelectionModel(); // CAS input/output cells createCASTable(); // row header rowHeader = new RowHeaderD(consoleTable, true, listSelModel, app); getConsoleTable().setSelectionModel(listSelModel); // init the scroll panel JScrollPane scrollPane = new JScrollPane( ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); scrollPane.setRowHeaderView(rowHeader); scrollPane.setViewportView(consoleTable); scrollPane.setBackground(Color.white); scrollPane.setBorder(BorderFactory.createEmptyBorder()); // set the lower left corner so that the horizontal scroller looks good JPanel p = new JPanel(); p.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 1, GColorD.getAwtColor(GeoGebraColorConstants.TABLE_GRID_COLOR))); p.setBackground(Color.white); scrollPane.setCorner(ScrollPaneConstants.LOWER_LEFT_CORNER, p); // put the scrollpanel in component.setLayout(new BorderLayout()); component.add(scrollPane, BorderLayout.CENTER); component.setBackground(Color.white); getConsoleTable().getSelectionModel() .addListSelectionListener(selectionListener()); // listen to clicks below last row in consoleTable: create new row scrollPane.addMouseListener(scrollPaneListener()); // input handler setCasInputHandler(new CASInputHandler(this)); // addFocusListener(this); // Create new DragGestureListener and enable Drag CASDragGestureListener dragGestListener = new CASDragGestureListener( kernel, consoleTable); dragGestListener.enableDnD(); // Create new CASDropTargetListener and enable Drop CASDropTargetListener dropTargetListener = new CASDropTargetListener( app, this, consoleTable); dropTargetListener.enableDnD(); updateFonts(); Thread initCAS = new Thread() { @Override public void run() { getCAS().initCurrentCAS(); GuiManagerD gm = (GuiManagerD) app.getGuiManager(); if (gm != null) { gm.reInitHelpPanel(true); } } }; initCAS.start(); } private ListSelectionListener selectionListener() { return new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } // table selection changed -> update stylebar int[] selRows = getConsoleTable().getSelectedRows(); if (selRows.length > 0) { // update list of selected objects in the stylebar ArrayList<GeoElement> targetCells = new ArrayList<GeoElement>(); for (int i = 0; i < getConsoleTable().getRowCount(); i++) { GeoElement cell = getConsoleTable() .getGeoCasCell(selRows[0]); if (cell != null) { targetCells.add(cell); } } if (styleBar != null) { styleBar.setSelectedRows(targetCells); } } } }; } private MouseListener scrollPaneListener() { return new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { int clickedRow = getConsoleTable().rowAtPoint(e.getPoint()); // boolean undoNeeded = false; if (clickedRow < 0) { // clicked outside of console table int rows = getConsoleTable().getRowCount(); if (rows == 0) { // insert first row insertRow(null, true); // undoNeeded = true; } else { getConsoleTable().stopEditing(); GeoCasCell cellValue = getConsoleTable() .getGeoCasCell(rows - 1); if (!cellValue.isInputEmpty() && cellValue.isOutputEmpty()) { getConsoleTable().startEditingRow(rows - 1); processInput("Evaluate", true); ensureOneEmptyRow(); getConsoleTable() .startEditingRow(getRowCount() - 1); } else { insertRow(null, true); // undoNeeded = true; } } } // if (undoNeeded) { // // store undo info // getApp().storeUndoInfo(); // } } }; } @Override public void showSubstituteDialog(final String prefix, final String evalText, final String postfix, final int selRow) { if (subDialog != null && subDialog.isShowing()) { return; } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { CASSubDialogD d = new CASSubDialogD(getCASViewD(), prefix, evalText, postfix, selRow); d.setAlwaysOnTop(true); d.setVisible(true); setSubstituteDialog(d); } }); } /** * Make sure this view knows whether substitute dialog is open * * @param d * substitute dialog; null to "close" */ public void setSubstituteDialog(CASSubDialogD d) { subDialog = d; } /** * Updates GUI fonts */ public void updateFonts() { if (component.getFont() != null && app.getGUIFontSize() == component.getFont().getSize()) { return; } component.setFont(app.getPlainFont()); getConsoleTable().setFont(component.getFont()); if (rowHeader != null) { rowHeader.updateIcons(); } getCASStyleBar().reinit(); // make sure the row header resizes with the table SwingUtilities.updateComponentTreeUI(component); } private void createCASTable() { consoleTable = new CASTableD(this); CASTableCellControllerD inputListener = new CASTableCellControllerD( this); getConsoleTable().getEditor().getInputArea() .addKeyListener(inputListener); getConsoleTable().getEditor().getInputArea() .addMouseListener(inputListener); } @Override public CASTableD getConsoleTable() { return consoleTable; } /** * Component representation of this view * * @return reference to self */ public JComponent getCASViewComponent() { return component; } /** * @return row headers */ public RowHeaderD getRowHeader() { return rowHeader; } /** * @return application of this view */ @Override public AppD getApp() { return app; } @Override public void repaintView() { component.repaint(); } @Override public AppD getApplication() { return app; } @Override public int[] getGridColwidths() { return new int[] { rowHeader.getWidth() + getConsoleTable().getWidth() }; } @Override public int[] getGridRowHeights() { int[] heights = new int[getConsoleTable().getRowCount()]; for (int i = 0; i < heights.length; i++) { heights[i] = getConsoleTable().getRowHeight(i); } return heights; } @Override public Component[][] getPrintComponents() { return new Component[][] { { rowHeader, consoleTable } }; } /** * Returns stylebar for this view; if not initialized so far, creates new * one * * @return style bar */ public CASStyleBar getCASStyleBar() { if (styleBar == null) { styleBar = newCASStyleBar(); } return styleBar; } /** * @return new instance of CASStyleBar */ protected CASStyleBar newCASStyleBar() { return new CASStyleBar(this, app); } @Override public boolean hasFocus() { Log.debug("unimplemented"); return false; } @Override public boolean isShowing() { Log.debug("unimplemented"); return false; } /** * @return this casView */ public CASViewD getCASViewD() { return this; } @Override public boolean suggestRepaint() { return false; // only used in web } @Override public void setOrientation() { getConsoleTable().setOrientation(); } /** * Stop editing */ public void resetCursor() { CASTable table = getConsoleTable(); table.stopEditing(); } /** * @return list model */ public ListSelectionModel getListSelModel() { return listSelModel; } public void resetItems(boolean unselectAll) { // nothing to do in desktop } }