Table of Contents
How to add a new command
Markus Hohenwarter, May 5, 2007
This document describes how to write code for a new command in GeoGebra's input field. I try to explain this using one very simple command, the midpoint of two points P and Q: Midpoint[P, Q].
IMPORTANT NOTE: When you create a new class provide YOUR NAME and the DATE at the BEGINNING of the file.
0. Before coding
Choose a suitable command name and command syntax and send those to translation@… so that they can be added to properties.
Midpoint=Midpoint MidpointSyntax=Midpoint[ <Point>, <Point> ]\nMidpoint[ <Segment> ]\nMidpoint[ <Conic> ]
1. Create representation in enums
First create a value in geogebra.common.kernel.commands.Commands; it must be the same as the chosen name of your command (incl. capitalization)! The constructor parameter lets you set in which category the command will appear in input help.
Now create enum value for the algorithm(s) this command will use. More complicated commands may be handled by more algorithms, but typically you need just one. Choose a class name for the algorithm (prepend Algo to the command name) and add this name to the enum geogebra.common.kernel.algos.Algos. The constructor parameter lets you connect this algorithm to the command -- that is needed for file saving. Second parameter is for Intergeo, in most cases it is not needed.
2. Create a new algorithm class
Such classes are located in geogebra.common.kernel.algos.
The best way to do this, is by copying an existing algorithm class and changing it. I will explain the following steps using the class geogebra.kernel.AlgoMidpoint as an example.
- Provide a constructor that takes the label of the resulting object, e.g. "M" for M = Midpoint[P, Q], and the input objects, e.g. Points P and Q.
- Within the constructor method make sure to call the methods setInputOutput() and compute()
- Change the following methods in your new algorithm class
- getClassName(): returns the corresponding Algos object
- setInputOutput(): make sure to call setDependencies() at its end
- compute(): here the actual work of your algorithm is done, e.g. the midpoint of P and Q is calculated
- toString(StringTemplate tpl): returns the definition representation of the algorithm that will be shown in the construction protocol and algebra window tooltips. Here you might need to have localized Strings. Usually a command name is localized by calling
app.getCommand("Midpoint"). However, you may also add new strings here but keep in mind that you have to document this
carefully as they will have to be translated later on (to many languages!). So please avoid to introduce unnecessary new strings
here. In order to only return the command description here (e.g. "Midpoint[A, B]" for this example) either omit this method or explicitly use
final public String toString(StringTemplate tpl) { return getCommandDescription(tpl); }
- provide a method that returns the resulting object of the algorithm, e.g. getPoint() returns the midpoint object of geogebra.common.kernel.algos.AlgoMidpoint
3. Add a new method for your command to geogebra.common.kernel.Kernel
This method's parameterlist should provide a label for the command's result and its input parameters, e.g. for our Midpoint command:
final public GeoPoint Midpoint(String label, GeoPoint P, GeoPoint Q) {
AlgoMidpoint algo = new AlgoMidpoint(cons, label, P, Q);
GeoPoint M = algo.getPoint();
return M;
}
The reason for this method is to provide a convenient way to use the command both from the command line of GeoGebra and in the geometry window when working with the mouse.
4. Create a new command processing class to geogebra.common.kernel.commands
Add a new subclass (a new file!) of CommandProcessor to the geogebra.kernel.commands package. The first three letters of the new classname should be "Cmd". You can find examples of such subclasses in geogebra.kernel.commands.CommandProcessor.java Here, the arguments of a command are processed and their types are checked in order to call the right variant of a command. For example the Midpoint command works for either a conic, a segment or two points.
/*
* Midpoint[ <GeoConic> ]
* Midpoint[ <Segment> ]
* Midpoint[ <GeoPoint>, <GeoPoint> ]
*/
class CmdMidpoint extends CommandProcessor {
public CmdMidpoint(Kernel kernel) {
super(kernel);
}
public GeoElement[] process(Command c) throws MyError {
int n = c.getArgumentNumber();
boolean[] ok = new boolean[n];
GeoElement[] arg;
switch (n) {
case 1 :
// Midpoint[ <GeoConic> ]
arg = resArgs(c);
if (ok[0] = (arg[0].isGeoConic())) {
GeoElement[] ret =
{ kernel.Center(c.getLabel(), (GeoConic) arg[0])};
return ret;
// Midpoint[ <GeoSegment> ]
} else if (arg[0].isGeoSegment()) {
GeoElement[] ret =
{ kernel.Midpoint(c.getLabel(), (GeoSegment) arg[0])};
return ret;
} else
throw argErr(app, c.getName(), arg[0]);
case 2 :
// Midpoint[ <GeoPoint>, <GeoPoint> ]
arg = resArgs(c);
if ((ok[0] = (arg[0].isGeoPoint()))
&& (ok[1] = (arg[1].isGeoPoint()))) {
GeoElement[] ret =
{
kernel.Midpoint(
c.getLabel(),
(GeoPoint) arg[0],
(GeoPoint) arg[1])};
return ret;
} else {
if (!ok[0])
throw argErr(app, c.getName(), arg[0]);
else
throw argErr(app, c.getName(), arg[1]);
}
default :
throw argNumErr(app, c.getName(), n);
}
}
}
5. Add a line to the switch in geogebra.common.kernel.commands.CommandDispatcher
Here we use the Commands enum value for our new command and associate it with our command processing class.
case Midpoint: return new CmdMidpoint(kernel);
