wiki:HowToAddANewCommand

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);