/*
EmulatorButtonBar.java
(c) 2011-2016 Edward Swartz
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
*/
package v9t9.gui.client.swt.bars;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TypedEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import v9t9.common.client.IMonitorEffectSupport;
import v9t9.common.client.ISoundHandler;
import v9t9.common.events.NotifyEvent.Level;
import v9t9.common.machine.IMachine;
import v9t9.common.settings.Settings;
import v9t9.common.sound.ISoundGenerator;
import v9t9.common.sound.ISoundRecordingHelper;
import v9t9.common.sound.MultiSoundOutputHandler;
import v9t9.common.sound.SoundRecordingHelper;
import v9t9.gui.client.swt.ISwtVideoRenderer;
import v9t9.gui.client.swt.SwtWindow;
import v9t9.gui.client.swt.imageimport.SwtImageImportSupport;
import v9t9.gui.client.swt.shells.ImageImportOptionsDialog;
import v9t9.gui.common.BaseEmulatorWindow;
import v9t9.gui.sound.ISwtSoundRecordingHelper;
import v9t9.gui.sound.SwtSoundRecordingHelper;
import ejs.base.properties.IProperty;
import ejs.base.properties.IPropertyListener;
import ejs.base.sound.ISoundOutput;
import ejs.base.utils.TextUtils;
/**
* This is the bar of command buttons on the right-hand side of the main emulator window.
* This has the main controls for the emulator's state, video, and sound settings.
*
* @author ejs
*
*/
public class EmulatorButtonBar extends BaseEmulatorBar {
private MultiSoundOutputHandler multiSoundHandler;
/**
* @param parent
* @param isHorizontal
* @param parent
* @param style
* @param focusRestorer
* @param smoothResize
*/
public EmulatorButtonBar(final SwtWindow window, IImageProvider imageProvider_, Composite parent,
final IMachine machine,
int[] colors, float[] points, int style) {
super(window, imageProvider_, parent, machine, colors, points, style);
if ((style & SWT.HORIZONTAL) != 0) {
GridData gd = ((GridData) buttonBar.getLayoutData());
gd.verticalSpan = 2;
}
buttonBar.addControlListener(new ControlAdapter() {
@Override
public void controlMoved(ControlEvent e) {
swtWindow.recenterToolShells();
}
@Override
public void controlResized(ControlEvent e) {
swtWindow.recenterToolShells();
}
});
buttonBar.getDisplay().addFilter(SWT.MouseUp, new Listener() {
public void handleEvent(Event event) {
if (!(event.widget instanceof Control))
return;
if (((Control) event.widget).getShell() != swtWindow.getShell())
return;
if (event.button == 1) {
Point pt = ((Control)event.widget).toDisplay(event.x, event.y);
swtWindow.handleClickOutsideToolWindow(pt);
}
}
});
BasicButton reset = createButton(IconConsts.RESET, "Reset the computer",
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
machine.getCpu().reset();
}
});
reset.addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(MouseEvent e) {
machine.notifyEvent(Level.INFO, "Hard reset, reloading");
machine.reset();
}
});
createToggleStateButton(IMachine.settingPauseMachine,
IconConsts.PAUSE,
IconConsts.CHECKMARK_OVERLAY, "Pause machine");
createButton(IconConsts.PASTE_KEYBOARD, "Paste into keyboard",
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
swtWindow.pasteClipboardToKeyboard();
}
});
final BasicButton loadSave = createButton(IconConsts.LOAD_SAVE_STATE, "Load or save machine state");
final Rectangle toUpperLeftBounds = imageProvider.imageIndexToBounds(IconConsts.TO_UPPER_LEFT);
final Rectangle toLowerRightBounds = imageProvider.imageIndexToBounds(IconConsts.TO_LOWER_RIGHT);
final BaseImageButtonAreaHandler loadHandler = new BaseImageButtonAreaHandler() {
@Override
public boolean isInBounds(int x, int y, Point size) {
return x + (size.x - y) > size.x;
}
@Override
public boolean isActive() {
return true;
}
@Override
public String getTooltip() {
return "Load state";
}
/* (non-Javadoc)
* @see v9t9.gui.client.swt.bars.BaseImageButtonAreaHandler#mouseEnter()
*/
@Override
public void mouseEnter() {
super.mouseEnter();
loadSave.addImageOverlay(toUpperLeftBounds);
}
/* (non-Javadoc)
* @see v9t9.gui.client.swt.bars.BaseImageButtonAreaHandler#mouseExit()
*/
@Override
public void mouseExit() {
loadSave.removeImageOverlay(toUpperLeftBounds);
super.mouseExit();
}
};
final BaseImageButtonAreaHandler saveHandler = new BaseImageButtonAreaHandler() {
@Override
public boolean isInBounds(int x, int y, Point size) {
return x + (size.x - y) < size.x;
}
@Override
public boolean isActive() {
return true;
}
@Override
public String getTooltip() {
return "Save state";
}
/* (non-Javadoc)
* @see v9t9.gui.client.swt.bars.BaseImageButtonAreaHandler#mouseEnter()
*/
@Override
public void mouseEnter() {
super.mouseEnter();
loadSave.addImageOverlay(toLowerRightBounds);
}
/* (non-Javadoc)
* @see v9t9.gui.client.swt.bars.BaseImageButtonAreaHandler#mouseExit()
*/
@Override
public void mouseExit() {
loadSave.removeImageOverlay(toLowerRightBounds);
super.mouseExit();
}
};
loadSave.addAreaHandler(loadHandler);
loadSave.addAreaHandler(saveHandler);
loadSave.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (loadHandler.isInBounds(e.x, e.y, loadSave.getSize())) {
swtWindow.loadMachineState();
}
else if (saveHandler.isInBounds(e.x, e.y, loadSave.getSize())) {
swtWindow.saveMachineState();
}
}
});
if (window.getVideoRenderer().getMonitorEffectSupport() != null) {
BasicButton monitorEffectButton = createToggleStateButton(BaseEmulatorWindow.settingMonitorDrawing,
IconConsts.MONITOR_EFFECT,
IconConsts.CHECKMARK_OVERLAY, "Apply monitor effect to video");
monitorEffectButton.addAreaHandler(new ImageButtonMenuAreaHandler(imageProvider,
new IMenuHandler() {
@Override
public void fillMenu(Menu menu) {
fillMonitorEffectMenu(
window.getVideoRenderer().getMonitorEffectSupport(),
menu
);
}
}
));
}
ImageButton screenshotButton = createButton(IconConsts.SCREENSHOT, "Take screenshot",
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
takeScreenshot(e, false);
}
});
screenshotButton.addAreaHandler(new ImageButtonMenuAreaHandler(imageProvider,
new IMenuHandler() {
@Override
public void fillMenu(Menu menu) {
String cur = machine.getSettings().get(BaseEmulatorWindow.settingScreenShotsBase).getString();
if (!TextUtils.isEmpty(cur)) {
MenuItem current = new MenuItem(menu, SWT.NONE);
current.setEnabled(false);
current.setText("Base path: " + cur);
}
MenuItem saveAs = new MenuItem(menu, SWT.PUSH);
saveAs.setText("Save as...");
saveAs.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
takeScreenshot(e, true);
}
});
final IProperty prop = machine.getSettings().get(
BaseEmulatorWindow.settingScreenshotPlain);
MenuItem plainBitmap = new MenuItem(menu, SWT.CHECK);
plainBitmap.setText("Save plain bitmap");
plainBitmap.setSelection(prop.getBoolean());
plainBitmap.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
prop.setBoolean(!prop.getBoolean());
}
});
}
}));
final SwtImageImportSupport imageSupport = new SwtImageImportSupport(
machine,
swtWindow.getEventNotifier(), swtWindow.getVideoRenderer());
BasicButton imageImportButton = createButton(IconConsts.IMAGE_IMPORT, "Import image",
new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
swtWindow.toggleToolShell(ImageImportOptionsDialog.IMAGE_IMPORTER_ID,
ImageImportOptionsDialog.getToolShellFactory(buttonBar, imageSupport, swtWindow));
}
}
);
//imageSupport.setImageImportDnDControl(imageImportButton);
imageSupport.setImageImportDnDControl(((ISwtVideoRenderer) window.getVideoRenderer()).getControl());
imageImportButton.addAreaHandler(new ImageButtonMenuAreaHandler(imageProvider,
new IMenuHandler() {
@Override
public void fillMenu(Menu menu) {
ImageImportOptionsDialog.fillImageImportMenu(window, imageSupport, menu);
}
}));
createToggleStateButton(BaseEmulatorWindow.settingFullScreen,
IconConsts.FULLSCREEN, IconConsts.CHECKMARK_OVERLAY, "Toggle fullscreen");
multiSoundHandler = new MultiSoundOutputHandler(machine);
ISoundHandler handler = machine.getClient().getSoundHandler();
for (Map.Entry<ISoundGenerator, ISoundOutput> ent : handler.getGeneratorToOutputMap().entrySet()) {
ISoundGenerator generator = ent.getKey();
if (generator.getRecordingSettingSchema() == null)
continue;
SoundRecordingHelper helper = new SwtSoundRecordingHelper(machine, ent.getValue(),
generator.getRecordingSettingSchema(),
generator.getName(),
generator.getSoundFormat(),
generator.isSilenceRecorded());
multiSoundHandler.register(helper);
}
getButtonBar().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
multiSoundHandler.dispose();
}
});
final BasicButton soundButton = createStateButton(ISoundHandler.settingPlaySound,
true,
IconConsts.SOUND_SPEAKER, IconConsts.NO_OVERLAY,
true, "Sound options");
soundButton.addAreaHandler(new ImageButtonMenuAreaHandler(imageProvider,
new IMenuHandler() {
@Override
public void fillMenu(Menu menu) {
fillSoundMenu(machine, menu);
}
}));
final IProperty pauseRecordingProperty = machine.getSettings().get(ISoundHandler.settingPauseSoundRecording);
final Rectangle recordingOverlayBounds = imageProvider.imageIndexToBounds(IconConsts.RECORD_OVERLAY);
final Rectangle pauseOverlayBounds = imageProvider.imageIndexToBounds(IconConsts.PAUSE_OVERLAY);
IPropertyListener recordingListener = new IPropertyListener() {
public void propertyChanged(final IProperty setting) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (soundButton.isDisposed())
return;
if (multiSoundHandler.isRecording()) {
soundButton.addImageOverlay(recordingOverlayBounds);
if (pauseRecordingProperty.getBoolean())
soundButton.addImageOverlay(pauseOverlayBounds);
else
soundButton.removeImageOverlay(pauseOverlayBounds);
} else {
soundButton.removeImageOverlay(recordingOverlayBounds);
soundButton.removeImageOverlay(pauseOverlayBounds);
}
//soundButton.redraw();
}
});
}
};
multiSoundHandler.addListenerAndFire(recordingListener);
pauseRecordingProperty.addListener(recordingListener);
soundButton.addAreaHandler(new BaseImageButtonAreaHandler() {
@Override
public boolean isActive() {
return multiSoundHandler.isRecording();
}
@Override
public String getTooltip() {
return (pauseRecordingProperty.getBoolean() ? "Resume recording to " : "Pause recording to ")
+ TextUtils.catenateStrings(multiSoundHandler.getRecordings(), ", ");
}
/* (non-Javadoc)
* @see v9t9.gui.client.swt.bars.IImageButtonAreaHandler#isInBounds(int, int, org.eclipse.swt.graphics.Point)
*/
@Override
public boolean isInBounds(int x, int y, Point size) {
return x < size.x/2 && y < size.y/2;
}
/* (non-Javadoc)
* @see v9t9.gui.client.swt.bars.BaseImageButtonAreaHandler#mouseClicked(int)
*/
@Override
public boolean mouseDown(int button) {
if (button == 1) {
pauseRecordingProperty.setBoolean(!pauseRecordingProperty.getBoolean());
if (pauseRecordingProperty.getBoolean()) {
}
return true;
}
return false;
}
});
}
/**
* @param saveAs
*/
protected void takeScreenshot(TypedEvent e, boolean saveAs) {
if (saveAs)
machine.getSettings().get(BaseEmulatorWindow.settingScreenShotsBase).setValue("");
swtWindow.screenshot();
}
/*
private Menu createZoomMenu(final Control parent) {
final Menu menu = new Menu(parent);
return populateZoomMenu(menu);
}
private Menu populateZoomMenu(final Menu menu) {
int curZoom = videoRenderer.getZoom();
int[] zooms = { 1, 2, 3, 4, 5, 6, 7, 8 };
for (final int zoom : zooms) {
MenuItem item = new MenuItem(menu, SWT.RADIO);
item.setText("" + zoom);
if (zoom == curZoom)
item.setSelection(true);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
setScreenZoom(zoom);
EmulatorSettings.getInstance().getApplicationSettings().put("ZoomLevel", zoom);
}
});
}
return menu;
}
protected void setScreenZoom(int zoom) {
System.out.println("Set zoom to " + zoom);
videoRenderer.setZoom(zoom);
}
*/
/**
* @param machine
* @param soundHandler
* @param e
*/
private void fillSoundMenu(final IMachine machine, Menu menu) {
for (ISoundRecordingHelper helper : multiSoundHandler.getRecordingHelpers()) {
if (helper instanceof ISwtSoundRecordingHelper)
((ISwtSoundRecordingHelper) helper).populateSoundMenu(menu);
}
MenuItem vitem = new MenuItem(menu, SWT.CASCADE);
vitem.setText("Volume");
final Menu volumeMenu = new Menu(vitem);
final IProperty soundVolume = Settings.get(machine, ISoundHandler.settingSoundVolume);
int curVol = soundVolume.getInt();
int[] vols = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for (final int vol : vols) {
MenuItem item = new MenuItem(volumeMenu, SWT.RADIO);
item.setText("" + vol);
if (vol == curVol)
item.setSelection(true);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
soundVolume.setInt(vol);
}
});
}
vitem.setMenu(volumeMenu);
}
/**
* @param machine
* @param soundHandler
* @param e
*/
private void fillMonitorEffectMenu(IMonitorEffectSupport fxSupport, Menu menu) {
final IProperty monitorEffect = machine.getSettings().get(BaseEmulatorWindow.settingMonitorEffect);
for (final String effectId : fxSupport.getIds()) {
MenuItem item = new MenuItem(menu, SWT.RADIO);
item.setText(fxSupport.getEffect(effectId).getLabel());
if (effectId.equals(monitorEffect.getString()))
item.setSelection(true);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
monitorEffect.setString(effectId);
}
});
}
}
}