// This file is part of Penn TotalRecall <http://memory.psych.upenn.edu/TotalRecall>.
//
// TotalRecall 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, version 3 only.
//
// TotalRecall is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TotalRecall. If not, see <http://www.gnu.org/licenses/>.
package components;
import info.SysInfo;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.UIManager;
import behaviors.UpdatingAction;
import behaviors.multiact.Last200PlusMoveAction;
import behaviors.multiact.OpenAudioLocationAction;
import behaviors.multiact.ScreenSeekAction;
import behaviors.multiact.SeekAction;
import behaviors.multiact.ToggleAnnotationsAction;
import behaviors.multiact.ZoomAction;
import behaviors.singleact.AboutAction;
import behaviors.singleact.CheckUpdatesAction;
import behaviors.singleact.DoneAction;
import behaviors.singleact.EditShortcutsAction;
import behaviors.singleact.ExitAction;
import behaviors.singleact.OpenWordpoolAction;
import behaviors.singleact.PlayPauseAction;
import behaviors.singleact.PreferencesAction;
import behaviors.singleact.ReplayLast200MillisAction;
import behaviors.singleact.ReplayLastPositionAction;
import behaviors.singleact.ReturnToLastPositionAction;
import behaviors.singleact.StopAction;
import behaviors.singleact.TipsMessageAction;
import behaviors.singleact.VisitTutorialSiteAction;
/**
* Program menu bar and trigger for updates in program's state that concern program actions.
*
* <p>Most of the program's user actions are tied to menu items in this <code>JMenu</code>.
* Programmers adding new functionality should in almost all cases add a corresponding <code>JMenuItem</code> here.
* However, there are those cases where an Action should only be initiated in another way (e.g., hitting enter in a text field), for which a menu item is inappropriate.
*
* <p>MyMenu keeps track of all {@link UpdatingAction} instances registered with it.
* Any time a change occurs in program state that might cause certain actions to enable/disable themselves, or otherwise change themselves, <code>MyMenu</code> will call their update method.
* This is why all program actions should inherit <code>UpdatingAction</code> (via <code>IdentifiedSingleAction</code> or <code>IdentifiedMutliAction</code>), as the abstract class
* takes care of registration automatically for you.
*
* See the the behaviors class for more information.
*
* @author Yuvi Masory
*/
public class MyMenu extends JMenuBar {
private static PlayPauseAction workaroundAction;
private static Set<UpdatingAction> allActions;
private static MyMenu instance;
private static String annotator;
private static boolean macLF;
/**
* Creates a new instance of the object, filling the menus and creating the actions.
*/
private MyMenu() {
allActions = new HashSet<UpdatingAction>();
macLF = SysInfo.sys.isMacOSX && (UIManager.getLookAndFeel().getClass().getName().equals(UIManager.getSystemLookAndFeelClassName()));
initFileMenu();
initControlsMenu();
initAnnotationMenu();
// initViewMenu();
initHelpMenu();
}
/**
* Creates the File menu, only adding exit and preferences options for non-OSX platforms.
*/
private void initFileMenu() {
JMenu jmFile = new JMenu("File");
JMenuItem jmiOpenWordpool = new JMenuItem(
new OpenWordpoolAction());
if(SysInfo.sys.useAWTFileChoosers) {
OpenAudioLocationAction openFileAction = new OpenAudioLocationAction(OpenAudioLocationAction.SelectionMode.FILES_ONLY);
JMenuItem jmiOpenAudioFile = new JMenuItem(openFileAction);
OpenAudioLocationAction openFolderAction = new OpenAudioLocationAction(OpenAudioLocationAction.SelectionMode.DIRECTORIES_ONLY);
JMenuItem jmiOpenAudioFolder = new JMenuItem(openFolderAction);
jmFile.add(jmiOpenAudioFile);
jmFile.add(jmiOpenAudioFolder);
}
else {
JMenuItem jmiOpenAudio = new JMenuItem(
new OpenAudioLocationAction(OpenAudioLocationAction.SelectionMode.FILES_AND_DIRECTORIES));
jmFile.add(jmiOpenAudio);
}
jmFile.add(jmiOpenWordpool);
JMenuItem jmiShortcuts = new JMenuItem(new EditShortcutsAction());
jmFile.add(jmiShortcuts);
if(macLF == false) {
jmFile.addSeparator();
JMenuItem jmiPreferences = new JMenuItem(new PreferencesAction());
jmFile.add(jmiPreferences);
jmFile.addSeparator();
JMenuItem jmiExit = new JMenuItem(new ExitAction());
jmFile.add(jmiExit);
}
add(jmFile);
}
/**
* Creates the Controls menu which controls audio playback and position.
*/
private void initControlsMenu() {
JMenu jmAudio = new JMenu("Controls");
//see PlayPauseAction docs for explanation of this funniness
JMenuItem jmiPlayPause = new JMenuItem(
new PlayPauseAction(true));
workaroundAction = new PlayPauseAction(false);
jmiPlayPause.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
workaroundAction.actionPerformed(e);
}});
JMenuItem jmiStop = new JMenuItem(
new StopAction());
JMenuItem jmiReplay = new JMenuItem(
new ReplayLast200MillisAction());
JMenuItem jmiLastPos = new JMenuItem(
new ReturnToLastPositionAction());
JMenuItem jmiReplayLast = new JMenuItem(
new ReplayLastPositionAction());
JMenu jmSeek = new JMenu("Seek");
SeekAction seekSmallForward = new SeekAction(SeekAction.SeekAmount.FORWARD_SMALL);
JMenuItem jmiSeekForwardSmall = new JMenuItem(seekSmallForward);
SeekAction seekSmallBackward = new SeekAction(SeekAction.SeekAmount.BACKWARD_SMALL);
JMenuItem jmiSeekSmallBackward = new JMenuItem(seekSmallBackward);
SeekAction seekMediumForward = new SeekAction(SeekAction.SeekAmount.FORWARD_MEDIUM);
JMenuItem jmiSeekForwardMedium = new JMenuItem(seekMediumForward);
SeekAction seekMediumBackward = new SeekAction(SeekAction.SeekAmount.BACKWARD_MEDIUM);
JMenuItem jmiSeekBackwardMedium = new JMenuItem(seekMediumBackward);
SeekAction seekLargeForward = new SeekAction(SeekAction.SeekAmount.FORWARD_LARGE);
JMenuItem jmiSeekForwardLarge = new JMenuItem(seekLargeForward);
SeekAction seekLargeBackward = new SeekAction(SeekAction.SeekAmount.BACKWARD_LARGE);
JMenuItem jmiSeekBackwardLarge = new JMenuItem(seekLargeBackward);
JMenuItem jmiLast200MoveRight = new JMenuItem(
new Last200PlusMoveAction(Last200PlusMoveAction.Direction.FORWARD));
JMenuItem jmiLast200MoveLeft = new JMenuItem(
new Last200PlusMoveAction(Last200PlusMoveAction.Direction.BACKWARD));
JMenuItem jmiScreenForward = new JMenuItem(new ScreenSeekAction(ScreenSeekAction.Dir.FORWARD));
JMenuItem jmiScreenBackward = new JMenuItem(new ScreenSeekAction(ScreenSeekAction.Dir.BACKWARD));
jmSeek.add(jmiSeekForwardSmall);
jmSeek.add(jmiSeekSmallBackward);
jmSeek.add(jmiSeekForwardMedium);
jmSeek.add(jmiSeekBackwardMedium);
jmSeek.add(jmiSeekForwardLarge);
jmSeek.add(jmiSeekBackwardLarge);
jmSeek.add(jmiLast200MoveRight);
jmSeek.add(jmiLast200MoveLeft);
jmSeek.add(jmiScreenForward);
jmSeek.add(jmiScreenBackward);
jmAudio.add(jmiPlayPause);
jmAudio.add(jmiStop);
jmAudio.add(jmiReplay);
jmAudio.add(jmiLastPos);
jmAudio.add(jmiReplayLast);
jmAudio.add(jmSeek);
add(jmAudio);
}
/**
* Creates the annotation menu.
*/
private void initAnnotationMenu() {
JMenu jmAnnotation = new JMenu("Annotation");
JMenuItem jmiDone = new JMenuItem(
new DoneAction());
jmAnnotation.add(jmiDone);
JMenuItem jmiNextAnn = new JMenuItem(
new ToggleAnnotationsAction(ToggleAnnotationsAction.Direction.FORWARD));
jmAnnotation.add(jmiNextAnn);
JMenuItem jmiPrevAnn = new JMenuItem(
new ToggleAnnotationsAction(ToggleAnnotationsAction.Direction.BACKWARD));
jmAnnotation.add(jmiPrevAnn);
add(jmAnnotation);
}
/**
* Creates the View menu, which controls aspects of the waveform's appearance.
*/
@SuppressWarnings("unused")
private void initViewMenu() {
JMenu jmView = new JMenu("View");
JMenuItem jmiZoomIn = new JMenuItem(
new ZoomAction(ZoomAction.Direction.IN));
JMenuItem jmiZoomOut = new JMenuItem(
new ZoomAction(ZoomAction.Direction.OUT));
jmView.add(jmiZoomIn);
jmView.add(jmiZoomOut);
add(jmView);
}
/**
* Creates the help menu, only adding an about menu for non-OSX platforms.
*/
private void initHelpMenu() {
JMenu jmHelp = new JMenu("Help");
JMenuItem jmiVisitMemLab = new JMenuItem(
new VisitTutorialSiteAction());
JMenuItem jmiCheckUpdates = new JMenuItem(
new CheckUpdatesAction(true));
JMenuItem jmiKeys = new JMenuItem(
new TipsMessageAction());
jmHelp.add(jmiVisitMemLab);
jmHelp.add(jmiCheckUpdates);
jmHelp.add(jmiKeys);
if(macLF == false) {
jmHelp.addSeparator();
JMenuItem jmiAbout = new JMenuItem(
new AboutAction());
jmHelp.add(jmiAbout);
}
add(jmHelp);
}
/**
* Trigger for updating all actions.
*
* Should be called anytime program state changes in a way that may interest the registered actions.
* Simple calls {@link UpdatingAction#update()} on every registered action.
*/
public static void updateActions() {
for(Object ia: allActions.toArray()) {
((UpdatingAction)ia).update();
}
}
public static void updateSeekActions() {
for(UpdatingAction ia: allActions) {
if(ia instanceof SeekAction) {
((SeekAction)(ia)).updateSeekAmount();
}
else if(ia instanceof Last200PlusMoveAction) {
((Last200PlusMoveAction)(ia)).updateSeekAmount();
}
}
}
/**
* Registere the provided <code>UpdatingAction</code> to receive updates when program state changes.
*
* This method is normally called automatically by the <code>UpdatingAction</code> constructor.
* Actions that have already been added will not be added a second time.
*
* @param act The <code>UpdatingAction</code> that wishes to recieve updates calls
*/
public static void registerAction(UpdatingAction act) {
if(allActions.add(act) == false) {
System.err.println("double registration of: " + act);
}
}
/**
* Singleton accessor.
*
* @return The singleton <code>MyMenu</code>
*/
public static MyMenu getInstance() {
if (instance == null) {
instance = new MyMenu();
}
return instance;
}
public static void setAnnotator(String annotator) {
MyMenu.annotator = annotator;
}
public static String getAnnotator() {
return annotator;
}
}