/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.motorola.studio.android.emulator.device.ui;
import java.util.List;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
import com.motorola.studio.android.emulator.device.IAndroidDeviceConstants;
import com.motorola.studio.android.emulator.device.instance.options.IStartupOptionsConstants;
import com.motorola.studio.android.emulator.device.instance.options.StartupOption;
import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsGroup;
import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
/**
* DESCRIPTION:
* <br>
* This class implements the UI for showing all Android Emulator Device Instance startup options information.
* <br>
* It extends the AbstractPropertiesComposite so as to use its common functionalities.
* <br>
* RESPONSIBILITY:
* <br>
* - Show Android Emulator Device Instance main information on the UI
* <br>
* COLABORATORS:
* <br>
* AbstractPropertiesComposite: extends this class
* <br>
* USAGE:
* <br>
* This class should be added as a regular composite whenever startup options information on Android Emulator
* Device Instance is necessary to be shown and edited on the UI.
*/
public class StartupOptionsComposite extends AbstractPropertiesComposite implements
IStartupOptionsConstants
{
// The widget which displays the current command line used to pass the startup options
private Text commandLine;
private final IAndroidSkin skin;
private final int TABFOLDER_HEIGHT_HINT = 350;
private boolean canCalculateScale = true;
/**
* Creates a StartupOptionsComposite object.
*
* @param parent the parent composite
* @param canCalculateScale
*/
public StartupOptionsComposite(Composite parent, String startupOptions, IAndroidSkin skin,
boolean canCalculateScale)
{
super(parent);
this.skin = skin;
this.canCalculateScale = canCalculateScale;
StartupOptionsMgt.loadFromCommandLine(startupOptions);
createUI();
// Set context Help
PlatformUI.getWorkbench().getHelpSystem()
.setHelp(parent, IAndroidDeviceConstants.STARTUP_OPTIONS_HELP);
}
/**
* Create widgets for startup options
*/
private void createUI()
{
Composite mainComposite = this;
Layout mainLayout = new GridLayout();
mainComposite.setLayout(mainLayout);
// list of startup options groups
List<StartupOptionsGroup> startupOptionsGroupsList =
StartupOptionsMgt.getStartupOptionsGroupsList();
// list of startup options in each group
List<StartupOption> startupOptions = null;
// Create Tab Folder
final TabFolder tabFolder = new TabFolder(mainComposite, SWT.NULL);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);
data.heightHint = TABFOLDER_HEIGHT_HINT;
tabFolder.setLayoutData(data);
/*
* Iterate through Startup Groups
*/
for (StartupOptionsGroup startupOptionGroup : startupOptionsGroupsList)
{
// Create Tab Item
TabItem tabItem = new TabItem(tabFolder, SWT.NULL);
tabItem.setText(startupOptionGroup.getTitle());
Composite group = new Composite(tabFolder, SWT.NULL);
group.setLayout(new GridLayout(3, false));
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
tabItem.setControl(group);
// get startup options in this group
startupOptions = startupOptionGroup.getStartupOptions();
/*
* Iterate through Startup Options in this group
*/
for (final StartupOption startupOption : startupOptions)
{
// create a checkbox for each startup option
Button checkbox = new Button(group, SWT.CHECK);
checkbox.setSelection(startupOption.isChecked());
checkbox.setText(startupOption.getUserFriendlyName());
checkbox.setToolTipText(startupOption.getName() + ": " //$NON-NLS-1$
+ startupOption.getDescription());
startupOption.setCheckedWidget(checkbox);
checkbox.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
boolean checkedStatus = ((Button) e.widget).getSelection();
startupOption.setChecked(checkedStatus);
notifyCompositeChangeListeners();
}
});
GridData checkboxData = new GridData(SWT.NULL, SWT.FILL, false, false);
checkbox.setLayoutData(checkboxData);
// Create input fields depending on the startup option type
switch (startupOption.getType())
{
case TYPE_NONE:
// extend checkbox area along the line
checkboxData.widthHint = SWT.DEFAULT;
checkboxData.horizontalSpan = 3;
checkbox.setLayoutData(checkboxData);
break;
case TYPE_TEXT:
case TYPE_NUMBER:
createWidgetsForTextOrNumberType(group, startupOption);
break;
case TYPE_PATH:
createWidgetsForPathType(group, startupOption);
break;
default:
// none
}
}
}
/*
* Command Line area
*/
Composite commandLineArea = new Composite(mainComposite, SWT.NONE); // composite
commandLineArea.setLayout(new GridLayout(2, false));
data = new GridData(SWT.FILL, SWT.FILL, true, true);
commandLineArea.setLayoutData(data);
Label commandLineLabel = new Label(commandLineArea, SWT.NONE); // label
commandLineLabel.setText(""); //$NON-NLS-1$
data = new GridData(SWT.FILL, SWT.FILL, false, true);
commandLineLabel.setLayoutData(data);
commandLine = new Text(commandLineArea, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL); // text
data = new GridData(SWT.FILL, SWT.FILL, true, true);
commandLineArea.pack();
data.widthHint = commandLineArea.getBounds().width - commandLineLabel.getBounds().width;
data.heightHint = commandLineArea.getBounds().height;
commandLine.setLayoutData(data);
commandLine.setText(StartupOptionsMgt.getParamList());
commandLine.setEditable(false);
}
/**
* Create widgets to enable user to input data for fields of text or number type
*
* @param parent composite where the widgets will be attached to
* @param startupOption the corresponding startup option
*/
private void createWidgetsForTextOrNumberType(final Composite parent,
final StartupOption startupOption)
{
// create input text with calc button
if (startupOption.getName().equals(SCALE))
{
final Text inputText = new Text(parent, SWT.SINGLE | SWT.BORDER);
inputText.setText(startupOption.getValue());
startupOption.setValueWidget(inputText);
inputText.setLayoutData(new GridData(SWT.FILL, SWT.NULL, true, false));
inputText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
startupOption.setValue(inputText.getText());
notifyCompositeChangeListeners();
}
});
Button calc = new Button(parent, SWT.PUSH);
calc.setText(EmulatorNLS.UI_DpiScale_Calculator);
GridData calcData = new GridData(SWT.NULL, SWT.NULL, false, false);
calc.setLayoutData(calcData);
calc.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
DpiScaleCalculatorDialog dialog =
new DpiScaleCalculatorDialog(new Shell(parent.getShell()), skin);
if (dialog.open() == Dialog.OK)
{
for (StartupOptionsGroup group : StartupOptionsMgt
.getStartupOptionsGroupsList())
{
for (StartupOption startupOption : group.getStartupOptions())
{
if (startupOption.getName().equals(SCALE))
{
startupOption.setChecked(true);
startupOption.setValue(dialog.getResultScaleValue());
startupOption.updateUI();
}
}
}
}
}
});
calc.setEnabled(canCalculateScale);
if (!canCalculateScale)
{
ControlDecoration controlDecoration =
new ControlDecoration(inputText, SWT.LEFT | SWT.TOP);
controlDecoration
.setDescriptionText(EmulatorNLS.StartupOptionsComposite_Error_Loading_Skin_Cant_Calculate_Scale);
FieldDecoration fieldDecoration =
FieldDecorationRegistry.getDefault().getFieldDecoration(
FieldDecorationRegistry.DEC_WARNING);
controlDecoration.setImage(fieldDecoration.getImage());
}
}
// create input text
else if ((startupOption.getPreDefinedValues() == null)
|| (startupOption.getPreDefinedValues().size() == 0))
{
final Text inputText = new Text(parent, SWT.SINGLE | SWT.BORDER);
inputText.setText(startupOption.getValue());
startupOption.setValueWidget(inputText);
inputText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
inputText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
startupOption.setValue(inputText.getText());
notifyCompositeChangeListeners();
}
});
}
// create combobox
else
{
final Combo combo = new Combo(parent, SWT.READ_ONLY);
startupOption.setValueWidget(combo);
int selectedIndex = 0;
for (String preDefinedValue : startupOption.getPreDefinedValues())
{
combo.add(preDefinedValue);
if (startupOption.getValue().equals(preDefinedValue))
{
combo.select(selectedIndex);
}
else
{
selectedIndex++;
}
}
combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
combo.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
startupOption.setValue(combo.getText());
notifyCompositeChangeListeners();
}
});
}
}
/**
* Create widgets to enable user to input data for fields of path type
*
* @param parent composite where the widgets will be attached to
* @param startupOption the corresponding startup option
*/
private void createWidgetsForPathType(final Composite parent, final StartupOption startupOption)
{
// create input text
final Text pathText = new Text(parent, SWT.SINGLE | SWT.BORDER);
pathText.setText(startupOption.getValue());
startupOption.setValueWidget(pathText);
pathText.setLayoutData(new GridData(SWT.FILL, SWT.NULL, true, false));
pathText.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
startupOption.setValue(pathText.getText());
notifyCompositeChangeListeners();
}
});
// create browse button
Button pathBrowseButton = new Button(parent, SWT.PUSH);
pathBrowseButton.setText(EmulatorNLS.UI_General_BrowseButtonLabel);
GridData data = new GridData(SWT.NULL, SWT.NULL, false, false);
pathBrowseButton.setLayoutData(data);
pathBrowseButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
String selectedPath = null;
if (startupOption.getTypeDetails().equalsIgnoreCase(TYPE_PATH_DIR))
{
DirectoryDialog directoryDialog = new DirectoryDialog(getShell(), SWT.OPEN);
selectedPath = directoryDialog.open();
}
else
{
FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
String[] filterExtensions =
{
"*" + startupOption.getTypeDetails() //$NON-NLS-1$
};
fileDialog.setFilterExtensions(filterExtensions);
selectedPath = fileDialog.open();
}
if (selectedPath != null)
{
pathText.setText(selectedPath);
}
}
});
}
/**
* Update command line value
*
* @see com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite#notifyCompositeChangeListeners()
*/
@Override
protected void notifyCompositeChangeListeners()
{
commandLine.setText(StartupOptionsMgt.getParamList());
super.notifyCompositeChangeListeners();
}
/**
* Retrieves the error message associated to this composites current state.
* The order of precedence of error is the same as the fields displayed on the
* UI, which means errors on fields drawn first are shown with a higher precedence
* than the errors of fields drawn last.
* The instance description field is the only non required field.
*
* @return the error message, or <code>null</code> if there are no errors
*/
@Override
public String getErrorMessage()
{
String errMsg = null;
Status status = StartupOptionsMgt.validate();
if (status.getSeverity() == Status.ERROR)
{
errMsg = status.getMessage();
}
return errMsg;
}
/**
* Reload the values being displayed in the UI as well as the ones
* in the model.
*
* @param startupOptions commandLine the command line used to start the emulator
*/
public void reloadValues(String commandLine)
{
StartupOptionsMgt.loadFromCommandLine(commandLine);
}
}