001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2012. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj.buttons;
009
010import edu.wpi.first.wpilibj.Sendable;
011import edu.wpi.first.wpilibj.command.Command;
012import edu.wpi.first.wpilibj.command.Scheduler;
013import edu.wpi.first.wpilibj.tables.ITable;
014
015/**
016 * This class provides an easy way to link commands to inputs.
017 *
018 * It is very easy to link a button to a command.  For instance, you could
019 * link the trigger button of a joystick to a "score" command.
020 *
021 * It is encouraged that teams write a subclass of Trigger if they want to have
022 * something unusual (for instance, if they want to react to the user holding
023 * a button while the robot is reading a certain sensor input).  For this, they
024 * only have to write the {@link Trigger#get()} method to get the full functionality
025 * of the Trigger class.
026 *
027 * @author Joe Grinstead
028 */
029public abstract class Trigger implements Sendable {
030
031    /**
032     * Returns whether or not the trigger is active
033     *
034     * This method will be called repeatedly a command is linked to the Trigger.
035     *
036     * @return whether or not the trigger condition is active.
037     */
038    public abstract boolean get();
039
040    /**
041     * Returns whether get() return true or the internal table for SmartDashboard use is pressed.
042     * @return  whether get() return true or the internal table for SmartDashboard use is pressed
043     */
044    private boolean grab() {
045        return get() || (table != null /*&& table.isConnected()*/ && table.getBoolean("pressed", false));//FIXME make is connected work?
046    }
047
048    /**
049     * Starts the given command whenever the trigger just becomes active.
050     * @param command the command to start
051     */
052    public void whenActive(final Command command) {
053        new ButtonScheduler() {
054
055            boolean pressedLast = grab();
056
057            public void execute() {
058                if (grab()) {
059                    if (!pressedLast) {
060                        pressedLast = true;
061                        command.start();
062                    }
063                } else {
064                    pressedLast = false;
065                }
066            }
067        } .start();
068    }
069
070    /**
071     * Constantly starts the given command while the button is held.
072     *
073     * {@link Command#start()} will be called repeatedly while the trigger is active,
074     * and will be canceled when the trigger becomes inactive.
075     *
076     * @param command the command to start
077     */
078    public void whileActive(final Command command) {
079        new ButtonScheduler() {
080
081            boolean pressedLast = grab();
082
083            public void execute() {
084                if (grab()) {
085                    pressedLast = true;
086                    command.start();
087                } else {
088                    if (pressedLast) {
089                        pressedLast = false;
090                        command.cancel();
091                    }
092                }
093            }
094        } .start();
095    }
096
097    /**
098     * Starts the command when the trigger becomes inactive
099     * @param command the command to start
100     */
101    public void whenInactive(final Command command) {
102        new ButtonScheduler() {
103
104            boolean pressedLast = grab();
105
106            public void execute() {
107                if (grab()) {
108                    pressedLast = true;
109                } else {
110                    if (pressedLast) {
111                        pressedLast = false;
112                        command.start();
113                    }
114                }
115            }
116        } .start();
117    }
118
119    /**
120      * Toggles a command when the trigger becomes active
121      * @param command the command to toggle
122      */
123    public void toggleWhenActive(final Command command) {
124        new ButtonScheduler() {
125
126            boolean pressedLast = grab();
127
128            public void execute() {
129                if (grab()) {
130                    if (!pressedLast) {
131                        pressedLast = true;
132                        if (command.isRunning()) {
133                            command.cancel();
134                        } else {
135                            command.start();
136                        }
137                    }
138                } else {
139                    pressedLast = false;
140                }
141            }
142        } .start();
143    }
144
145    /**
146      * Cancels a command when the trigger becomes active
147      * @param command the command to cancel
148      */
149    public void cancelWhenActive(final Command command) {
150        new ButtonScheduler() {
151
152            boolean pressedLast = grab();
153
154            public void execute() {
155                if (grab()) {
156                    if (!pressedLast) {
157                        pressedLast = true;
158                        command.cancel();
159                    }
160                } else {
161                    pressedLast = false;
162                }
163            }
164        } .start();
165    }
166
167    /**
168     * An internal class of {@link Trigger}.  The user should ignore this, it is
169     * only public to interface between packages.
170     */
171    public abstract class ButtonScheduler {
172        public abstract void execute();
173
174        protected void start() {
175            Scheduler.getInstance().addButton(this);
176        }
177    }
178
179    /**
180     * These methods continue to return the "Button" SmartDashboard type until we decided
181     * to create a Trigger widget type for the dashboard.
182     */
183    public String getSmartDashboardType() {
184        return "Button";
185    }
186    private ITable table;
187    public void initTable(ITable table) {
188        this.table = table;
189        if(table!=null) {
190            table.putBoolean("pressed", get());
191        }
192    }
193
194    /**
195     * {@inheritDoc}
196     */
197    public ITable getTable() {
198        return table;
199    }
200}