/* 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; import java.awt.Color; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JCheckBoxMenuItem; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import org.geogebra.common.gui.ContextMenuGeoElement; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.arithmetic.TextValue; import org.geogebra.common.kernel.geos.Animatable; import org.geogebra.common.kernel.geos.GeoBoolean; import org.geogebra.common.kernel.geos.GeoConic; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoElement.TraceModesEnum; import org.geogebra.common.kernel.geos.GeoLine; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.kernel.geos.GeoNumeric; import org.geogebra.common.kernel.geos.GeoSegment; import org.geogebra.common.kernel.geos.Traceable; import org.geogebra.common.kernel.implicit.GeoImplicit; import org.geogebra.common.kernel.kernelND.CoordStyle; import org.geogebra.common.kernel.kernelND.GeoConicND; import org.geogebra.common.kernel.kernelND.ViewCreator; import org.geogebra.common.main.App; import org.geogebra.common.main.Localization; import org.geogebra.common.plugin.EventType; import org.geogebra.desktop.gui.util.LayoutUtil; import org.geogebra.desktop.main.AppD; import org.geogebra.desktop.util.GuiResourcesD; /** * Context menu for GeoElement objects. * * @author Markus Hohenwarter */ public class ContextMenuGeoElementD extends ContextMenuGeoElement { /** background color */ protected final static Color bgColor = Color.white; /** foreground color */ protected final static Color fgColor = Color.black; /** the actual menu */ protected JPopupMenu wrappedPopup; /** localization */ protected final Localization loc; /** * Creates new context menu * * @param app * application */ ContextMenuGeoElementD(AppD app) { super(app); this.loc = app.getLocalization(); this.wrappedPopup = new JPopupMenu(); wrappedPopup.setBackground(bgColor); } /** * Creates new MyPopupMenu for GeoElement * * @param app * application * @param geos * selected elements * @param location * screen position */ public ContextMenuGeoElementD(AppD app, ArrayList<GeoElement> geos, Point location) { this(app); this.setGeos(geos); setGeo(geos.get(0)); String title; if (geos.size() == 1) { title = getDescription(getGeo(), true); } else { title = loc.getMenu("Selection"); } setTitle(title); if (app.getGuiManager().showView(App.VIEW_ALGEBRA)) { addCoordsModeItems(); if (app.getSettings().getCasSettings().isEnabled()) { addLineItems(); addConicItems(); addNumberItems(); addUserInputItem(); } } // TODO remove the condition when ggb version >= 5 if (app.getKernel().getManager3D() != null) { addPlaneItems(); } if (wrappedPopup.getComponentCount() > 2) { wrappedPopup.addSeparator(); } addForAllItems(); app.setComponentOrientation(wrappedPopup); } private void addCoordsModeItems() { if (!(getGeo() instanceof CoordStyle) || getGeo() instanceof GeoLine) { return; } if (getGeo().isProtected(EventType.UPDATE)) { return; } CoordStyle point = (CoordStyle) getGeo(); int mode = point.getMode(); AbstractAction action; switch (mode) { case Kernel.COORD_COMPLEX: default: return; // 2D coords styles case Kernel.COORD_POLAR: action = new AbstractAction(loc.getMenu("CartesianCoords")) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { cartesianCoordsCmd(); } }; addAction(action); break; case Kernel.COORD_CARTESIAN: action = new AbstractAction(loc.getMenu("PolarCoords")) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { polarCoorsCmd(); } }; addAction(action); break; // 3D coords styles case Kernel.COORD_SPHERICAL: action = new AbstractAction(loc.getMenu("CartesianCoords")) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { cartesianCoords3dCmd(); } }; addAction(action); break; case Kernel.COORD_CARTESIAN_3D: action = new AbstractAction(loc.getMenu("Spherical")) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { sphericalCoordsCmd(); } }; addAction(action); break; } } private void addLineItems() { if (!(getGeo() instanceof GeoLine)) { return; } if (getGeo() instanceof GeoSegment) { return; } GeoLine line = (GeoLine) getGeo(); int mode = line.getMode(); AbstractAction action; StringBuilder sb = new StringBuilder(); if (mode != GeoLine.EQUATION_IMPLICIT) { sb.setLength(0); sb.append(loc.getMenu("Equation")); sb.append(' '); sb.append(loc.getMenu("ImplicitLineEquation")); action = new AbstractAction(sb.toString()) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { equationImplicitEquationCmd(); } }; addAction(action); } if (mode != GeoLine.EQUATION_EXPLICIT) { sb.setLength(0); sb.append(loc.getMenu("Equation")); sb.append(' '); sb.append(loc.getMenu("ExplicitLineEquation")); action = new AbstractAction(sb.toString()) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { equationExplicitEquationCmd(); } }; addAction(action); } if (mode != GeoLine.PARAMETRIC) { action = new AbstractAction(loc.getMenu("ParametricForm")) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { parametricFormCmd(); } }; addAction(action); } if (mode != GeoLine.EQUATION_GENERAL) { sb.setLength(0); sb.append(loc.getMenu("Equation")); sb.append(' '); sb.append(loc.getMenu("GeneralLineEquation")); action = new AbstractAction(sb.toString()) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { equationGeneralLineEquationCmd(); } }; addAction(action); } } private void addConicItems() { if (getGeo().getClass() != GeoConic.class) { return; } GeoConic conic = (GeoConic) getGeo(); // there's no need to show implicit equation // if you can't select the specific equation boolean specificPossible = conic.isSpecificPossible(); boolean explicitPossible = conic.isExplicitPossible(); boolean vertexformPossible = conic.isVertexformPossible(); boolean conicformPossible = conic.isConicformPossible(); if (!(specificPossible || explicitPossible)) { return; } int mode = conic.getToStringMode(); AbstractAction action; StringBuilder sb = new StringBuilder(); if (mode != GeoConicND.EQUATION_IMPLICIT) { sb.append(loc.getMenu("Equation")); sb.append(' '); sb.append(loc.getMenu("ImplicitConicEquation")); action = new AbstractAction(sb.toString()) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { implicitConicEquationCmd(); } }; addAction(action); } if (specificPossible && mode != GeoConicND.EQUATION_SPECIFIC) { // specific conic string String conicEqn = conic.getSpecificEquation(); if (conicEqn != null) { sb.setLength(0); sb.append(loc.getMenu("Equation")); sb.append(' '); sb.append(conicEqn); action = new AbstractAction(sb.toString()) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { equationConicEqnCmd(); } }; addAction(action); } } if (explicitPossible && mode != GeoConicND.EQUATION_EXPLICIT) { sb.setLength(0); sb.append(loc.getMenu("Equation")); sb.append(' '); sb.append(loc.getMenu("ExplicitConicEquation")); action = new AbstractAction(sb.toString()) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { equationExplicitConicEquationCmd(); } }; addAction(action); } if (vertexformPossible && mode != GeoConicND.EQUATION_VERTEX) { sb.setLength(0); sb.append(loc.getMenu("Equation")); sb.append(' '); sb.append(loc.getMenu("ParabolaVertexForm")); action = new AbstractAction(sb.toString()) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { equationVertexEquationCmd(); } }; addAction(action); } if (conicformPossible && mode != GeoConicND.EQUATION_CONICFORM) { sb.setLength(0); sb.append(loc.getMenu("Equation")); sb.append(' '); sb.append(loc.getMenu("ParabolaConicForm")); action = new AbstractAction(sb.toString()) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { equationConicformEquationCmd(); } }; addAction(action); } } private void addUserInputItem() { if (getGeo() instanceof GeoImplicit) { final GeoImplicit inputElement = (GeoImplicit) getGeo(); if (inputElement.isValidInputForm()) { AbstractAction action; if (inputElement.isInputForm()) { action = new AbstractAction(loc.getMenu("ExtendedForm")) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { extendedFormCmd(inputElement); } }; } else { action = new AbstractAction(loc.getMenu("InputForm")) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { inputFormCmd(inputElement); } }; } addAction(action); } } } private void addNumberItems() { // no items } private void addPin() { if (getGeo().isPinnable()) { // GeoText geoText = (GeoText) geo; // show object final JCheckBoxMenuItem cbItem = new JCheckBoxMenuItem( loc.getMenu("AbsoluteScreenLocation")); ((AppD) app).setEmptyIcon(cbItem); cbItem.setIcon(((AppD) app).getScaledIcon(GuiResourcesD.PIN)); cbItem.setSelected(getGeo().isPinned()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { boolean isSelected = cbItem.isSelected(); pinCmd(isSelected); } }); addItem(cbItem); } } private void addPlaneItems() { if (!(getGeo() instanceof ViewCreator)) { return; } final ViewCreator plane = (ViewCreator) getGeo(); AbstractAction action; action = new AbstractAction(app.getLocalization() .getPlain("ShowAas2DView", getGeo().getLabelSimple())) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { plane.setView2DVisible(true); } }; addAction(action); } private void addForAllItems() { // SHOW, HIDE // G.Sturr 2010-5-14: allow menu to show spreadsheet trace for // non-drawables if (getGeo().isDrawable() || (getGeo().isSpreadsheetTraceable() && app.getGuiManager().showView(App.VIEW_SPREADSHEET))) { JCheckBoxMenuItem cbItem; // show object if (getGeo().isEuclidianShowable() && getGeo().getShowObjectCondition() == null && (!getGeo().isGeoBoolean() || getGeo().isIndependent())) { cbItem = new JCheckBoxMenuItem(loc.getMenu("ShowObject")); cbItem.setIcon(((AppD) app) .getScaledIcon(GuiResourcesD.MODE_SHOWHIDEOBJECT_GIF)); cbItem.setSelected(getGeo().isSetEuclidianVisible()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showObjectCmd(); } }); addItem(cbItem); } if (getGeo().isLabelShowable()) { // show label cbItem = new JCheckBoxMenuItem(loc.getMenu("ShowLabel")); cbItem.setSelected(isLabelShown()); cbItem.setIcon(((AppD) app) .getScaledIcon(GuiResourcesD.MODE_SHOWHIDELABEL)); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showLabelCmd(); } }); addItem(cbItem); } // trace if (getGeo().isTraceable()) { cbItem = new JCheckBoxMenuItem(loc.getMenu("TraceOn")); cbItem.setIcon( ((AppD) app).getScaledIcon(GuiResourcesD.TRACE_ON)); cbItem.setSelected(((Traceable) getGeo()).getTrace()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { traceCmd(); } }); addItem(cbItem); } // trace to spreadsheet: use SpreadsheetTrace Dialog if (app.getGuiManager().showView(App.VIEW_SPREADSHEET) && getGeo().hasSpreadsheetTraceModeTraceable()) { // if multiple geos selected, check if recordable as a list if (getGeos().size() == 1 || GeoList .getTraceModes(getGeos()) != TraceModesEnum.NOT_TRACEABLE) { cbItem = new JCheckBoxMenuItem( loc.getMenu("RecordToSpreadsheet")); cbItem.setIcon(((AppD) app) .getScaledIcon(GuiResourcesD.SPREADSHEETTRACE)); cbItem.setSelected(getGeo().getSpreadsheetTrace()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { recordToSpreadSheetCmd(); } }); addItem(cbItem); } } // animation if (getGeo().isAnimatable()) { cbItem = new JCheckBoxMenuItem(loc.getMenu("Animating")); ((AppD) app).setEmptyIcon(cbItem); cbItem.setSelected(((Animatable) getGeo()).isAnimating() && app.getKernel().getAnimatonManager().isRunning()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { animationCmd(); } }); addItem(cbItem); } // AUXILIARY OBJECT if (app.getGuiManager().showView(App.VIEW_ALGEBRA) && app.showAuxiliaryObjects() && getGeo().isAlgebraShowable()) { // show object cbItem = new JCheckBoxMenuItem(loc.getMenu("AuxiliaryObject")); cbItem.setIcon( ((AppD) app).getScaledIcon(GuiResourcesD.AUXILIARY)); cbItem.setSelected(getGeo().isAuxiliaryObject()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showObjectAuxiliaryCmd(); } }); addItem(cbItem); } // fix object if (getGeo().isFixable() && (getGeo().isGeoText() || getGeo().isGeoImage() || getGeo().isGeoButton())) { cbItem = new JCheckBoxMenuItem(loc.getMenu("FixObject")); ((AppD) app).setEmptyIcon(cbItem); cbItem.setSelected(getGeo().isLocked()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fixObjectCmd(); } }); addItem(cbItem); } else if (getGeo().isGeoNumeric()) { final GeoNumeric num = (GeoNumeric) getGeo(); if (num.isSlider()) { cbItem = new JCheckBoxMenuItem(loc.getMenu("FixObject")); ((AppD) app).setEmptyIcon(cbItem); cbItem.setSelected(num.isSliderFixed()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fixObjectNumericCmd(num); } }); addItem(cbItem); } } else if (getGeo().isGeoBoolean()) { cbItem = new JCheckBoxMenuItem(loc.getMenu("FixCheckbox")); ((AppD) app).setEmptyIcon(cbItem); cbItem.setSelected(((GeoBoolean) getGeo()).isCheckboxFixed()); cbItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fixCheckboxCmd(); } }); addItem(cbItem); } // Pinnable addPin(); wrappedPopup.addSeparator(); } // Rename if (getGeos().size() == 1 && app.letRename() && getGeo().isRenameable()) { addAction(new AbstractAction(loc.getMenu("Rename"), ((AppD) app).getScaledIcon(GuiResourcesD.RENAME)) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { renameCmd(); } }); } // EDITING // EDIT Text in special dialog if (getGeos().size() == 1 && getGeo() instanceof TextValue && !getGeo().isTextCommand() && !getGeo().isProtected(EventType.UPDATE)) { addAction(new AbstractAction(loc.getMenu("Edit"), ((AppD) app).getScaledIcon(GuiResourcesD.EDIT)) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { editCmd(); } }); } // DELETE if (app.letDelete() && !getGeo().isProtected(EventType.REMOVE)) { addAction(new AbstractAction(loc.getMenu("Delete"), ((AppD) app).getScaledIcon(GuiResourcesD.DELETE_SMALL)) { /** * */ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { deleteCmd(false); } }); } if (((AppD) app).letShowPropertiesDialog() && getGeo().hasProperties()) { wrappedPopup.addSeparator(); // open properties dialog addAction(new AbstractAction(loc.getMenu("Properties") + " ...", ((AppD) app) .getScaledIcon(GuiResourcesD.VIEW_PROPERTIES_16)) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { if (isJustOneGeo()) { app.getSelectionManager().setSelectedGeos(checkOneGeo(), true); } openPropertiesDialogCmd(); } }); } } /** * @return whether just one geo is selected */ protected boolean isJustOneGeo() { return justOneGeo; } /** * Adds given action to this menu * * @param ac * action */ void addAction(Action ac) { JMenuItem mi = wrappedPopup.add(ac); mi.setBackground(bgColor); } /** * Adds given item to this menu * * @param mi * item */ void addItem(JMenuItem mi) { mi.setBackground(bgColor); wrappedPopup.add(mi); } /** * Sets title of this menu; e.g. "Point A" or "Selection" * * @param str * title of this menu */ protected void setTitle(String str) { JLabel title = new JLabel(str); title.setFont(((AppD) app).getBoldFont()); title.setBackground(bgColor); title.setForeground(fgColor); title.setIcon(((AppD) app).getEmptyIcon()); title.setBorder(BorderFactory.createEmptyBorder(5, 0, 2, 15)); // wrap title JLabel in a panel to prevent unneeded spacing wrappedPopup.add(LayoutUtil.flowPanel(0, 0, 0, title)); wrappedPopup.addSeparator(); title.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { wrappedPopup.setVisible(false); } }); } /** * Adds keyboard shortcut to given itemof this menu * * @param mi * item * @param acc * accelerator */ protected void setMenuShortCutAccelerator(JMenuItem mi, char acc) { KeyStroke ks = KeyStroke.getKeyStroke(acc, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()); mi.setAccelerator(ks); } /** * @return the wrapped PopupMenu */ public JPopupMenu getWrappedPopup() { return this.wrappedPopup; } @Override public void recordToSpreadSheetCmd() { GeoElement geoRecordToSpreadSheet; if (getGeos().size() == 1) { geoRecordToSpreadSheet = getGeo(); } else { geoRecordToSpreadSheet = app.getKernel().getAlgoDispatcher() .List(null, getGeos(), false); geoRecordToSpreadSheet.setAuxiliaryObject(true); } ((GuiManagerD) app.getGuiManager()).getSpreadsheetView() .showTraceDialog(geoRecordToSpreadSheet, null); } }