001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2017. 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 * <p>It is very easy to link a button to a command. For instance, you could link the trigger
019 * button of a joystick to a "score" command.
020 *
021 * <p>It is encouraged that teams write a subclass of Trigger if they want to have something unusual
022 * (for instance, if they want to react to the user holding a button while the robot is reading a
023 * certain sensor input). For this, they only have to write the {@link Trigger#get()} method to get
024 * the full functionality of the Trigger class.
025 */
026public abstract class Trigger implements Sendable {
027
028  /**
029   * Returns whether or not the trigger is active.
030   *
031   * <p>This method will be called repeatedly a command is linked to the Trigger.
032   *
033   * @return whether or not the trigger condition is active.
034   */
035  public abstract boolean get();
036
037  /**
038   * Returns whether get() return true or the internal table for SmartDashboard use is pressed.
039   *
040   * @return whether get() return true or the internal table for SmartDashboard use is pressed.
041   */
042  @SuppressWarnings("PMD.UselessParentheses")
043  private boolean grab() {
044    return get() || (m_table != null && m_table.getBoolean("pressed", false));
045
046  }
047
048  /**
049   * Starts the given command whenever the trigger just becomes active.
050   *
051   * @param command the command to start
052   */
053  public void whenActive(final Command command) {
054    new ButtonScheduler() {
055
056      private boolean m_pressedLast = grab();
057
058      @Override
059      public void execute() {
060        if (grab()) {
061          if (!m_pressedLast) {
062            m_pressedLast = true;
063            command.start();
064          }
065        } else {
066          m_pressedLast = false;
067        }
068      }
069    }.start();
070  }
071
072  /**
073   * Constantly starts the given command while the button is held.
074   *
075   * {@link Command#start()} will be called repeatedly while the trigger is active, and will be
076   * canceled when the trigger becomes inactive.
077   *
078   * @param command the command to start
079   */
080  public void whileActive(final Command command) {
081    new ButtonScheduler() {
082
083      private boolean m_pressedLast = grab();
084
085      @Override
086      public void execute() {
087        if (grab()) {
088          m_pressedLast = true;
089          command.start();
090        } else {
091          if (m_pressedLast) {
092            m_pressedLast = false;
093            command.cancel();
094          }
095        }
096      }
097    }.start();
098  }
099
100  /**
101   * Starts the command when the trigger becomes inactive.
102   *
103   * @param command the command to start
104   */
105  public void whenInactive(final Command command) {
106    new ButtonScheduler() {
107
108      private boolean m_pressedLast = grab();
109
110      @Override
111      public void execute() {
112        if (grab()) {
113          m_pressedLast = true;
114        } else {
115          if (m_pressedLast) {
116            m_pressedLast = false;
117            command.start();
118          }
119        }
120      }
121    }.start();
122  }
123
124  /**
125   * Toggles a command when the trigger becomes active.
126   *
127   * @param command the command to toggle
128   */
129  public void toggleWhenActive(final Command command) {
130    new ButtonScheduler() {
131
132      private boolean m_pressedLast = grab();
133
134      @Override
135      public void execute() {
136        if (grab()) {
137          if (!m_pressedLast) {
138            m_pressedLast = true;
139            if (command.isRunning()) {
140              command.cancel();
141            } else {
142              command.start();
143            }
144          }
145        } else {
146          m_pressedLast = false;
147        }
148      }
149    }.start();
150  }
151
152  /**
153   * Cancels a command when the trigger becomes active.
154   *
155   * @param command the command to cancel
156   */
157  public void cancelWhenActive(final Command command) {
158    new ButtonScheduler() {
159
160      private boolean m_pressedLast = grab();
161
162      @Override
163      public void execute() {
164        if (grab()) {
165          if (!m_pressedLast) {
166            m_pressedLast = true;
167            command.cancel();
168          }
169        } else {
170          m_pressedLast = false;
171        }
172      }
173    }.start();
174  }
175
176  /**
177   * An internal class of {@link Trigger}. The user should ignore this, it is only public to
178   * interface between packages.
179   */
180  public abstract class ButtonScheduler {
181    public abstract void execute();
182
183    protected void start() {
184      Scheduler.getInstance().addButton(this);
185    }
186  }
187
188  /**
189   * These methods continue to return the "Button" SmartDashboard type until we decided to create a
190   * Trigger widget type for the dashboard.
191   */
192  @Override
193  public String getSmartDashboardType() {
194    return "Button";
195  }
196
197  private ITable m_table;
198
199  @Override
200  public void initTable(ITable table) {
201    m_table = table;
202    if (table != null) {
203      table.putBoolean("pressed", get());
204    }
205  }
206
207  @Override
208  public ITable getTable() {
209    return m_table;
210  }
211}