/*
* 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.generateviewbylayout.ui;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.PlatformUI;
import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
import com.motorola.studio.android.common.IAndroidConstants;
import com.motorola.studio.android.common.exception.AndroidException;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorola.studio.android.generatecode.JDTUtils;
import com.motorola.studio.android.generateviewbylayout.JavaModifierBasedOnLayout;
import com.motorola.studio.android.generateviewbylayout.model.CodeGeneratorDataBasedOnLayout;
import com.motorola.studio.android.generateviewbylayout.model.JavaLayoutData;
import com.motorola.studio.android.generateviewbylayout.model.LayoutNode;
/**
* Abstract dialog to deal with common methods for the UI dialog involved into the code generation
*/
public abstract class AbstractLayoutItemsDialog extends TitleAreaDialog
{
private JavaModifierBasedOnLayout modifier;
private ICompilationUnit javaFile;
private IProject javaProject;
private CodeGeneratorDataBasedOnLayout codeGeneratorData;
private Combo projectNameComboBox;
private Combo classNameComboBox;
private Label layoutFileNameLabel;
private TableViewer viewer;
private Button unselectAllButton;
private Button selectAllButton;
private String layoutFileErrorMessage;
private String helpID = null;
protected static final String DEFAULT_LAYOUT_FILE_NAME_LABEL_VALUE = "--"; //$NON-NLS-1$
private final String defaultMessage;
private final String title;
private final String shellTitle;
private final Image image;
public AbstractLayoutItemsDialog(Shell parentShell, String description, String title,
String shellTitle, Image image)
{
super(parentShell);
this.defaultMessage = description != null ? description : ""; //$NON-NLS-1$
this.title = title != null ? title : ""; //$NON-NLS-1$
this.shellTitle = shellTitle != null ? shellTitle : ""; //$NON-NLS-1$
this.image = image;
}
/**
* Initializes the modifier to enable code generation
* @param modifier the responsible to modify code
* @param javaProject the project of the Android file to modify
* @param javaFile the Android file to change
*/
public void init(JavaModifierBasedOnLayout modifier, IProject javaProject, IFile javaFile)
{
setModifier(modifier);
setJavaProject(javaProject);
setJavaFile(javaFile);
}
@Override
protected Control createContents(Composite parent)
{
Control c = super.createContents(parent);
setTitle(title);
if (image != null)
{
setTitleImage(image);
}
validate();
return c;
}
@Override
protected final Control createDialogArea(Composite parent)
{
if (helpID != null)
{
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, helpID);
}
Composite parentComposite = (Composite) super.createDialogArea(parent);
Composite mainComposite = new Composite(parentComposite, SWT.NONE);
mainComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
mainComposite.setLayout(new GridLayout(2, false));
createProjectNameArea(mainComposite);
createClassNameArea(mainComposite);
createLayoutFileNameArea(mainComposite);
Label separator = new Label(mainComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
separator.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1));
createTableArea(mainComposite);
createCustomContentArea(mainComposite);
separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
separator.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false, 2, 1));
return parentComposite;
}
/**
* Create a custom area in the end of the dialog
* The parent composite has a grid layout with two columns
* @param mainComposite
*/
protected abstract void createCustomContentArea(Composite mainComposite);
/**
* Create GUI items for project name selection.
* @param optionsComposite
*/
private void createProjectNameArea(Composite parent)
{
Label projectLabel = new Label(parent, SWT.NONE);
projectLabel.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_Project);
projectLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
projectNameComboBox = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
projectNameComboBox.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
projectNameComboBox.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
setJavaProject((IProject) projectNameComboBox.getData(projectNameComboBox.getText()));
setErrorMessage(null);
javaFile = null;
layoutFileErrorMessage = null;
codeGeneratorData = null;
populateClasses();
populateLayouts();
}
});
populateProjects();
}
/**
* Create GUI items for class name selection.
* @param parent
*/
private void createClassNameArea(Composite parent)
{
Label classLabel = new Label(parent, SWT.NONE);
classLabel.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_TargetClass);
classLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
classNameComboBox = new Combo(parent, SWT.READ_ONLY | SWT.DROP_DOWN);
classNameComboBox.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
classNameComboBox.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
setJavaFile(((IType) classNameComboBox.getData(classNameComboBox.getText()))
.getCompilationUnit());
codeGeneratorData = null;
populateLayouts();
}
});
populateClasses();
}
/**
* Create GUI items for layout file name selection.
* @param parent
*/
private void createLayoutFileNameArea(Composite parent)
{
Label layoutFileLabel = new Label(parent, SWT.NONE);
layoutFileLabel.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_SourceLayoutFile);
layoutFileLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
layoutFileNameLabel = new Label(parent, SWT.NONE);
layoutFileNameLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1));
layoutFileNameLabel.setText(DEFAULT_LAYOUT_FILE_NAME_LABEL_VALUE);
populateLayouts();
}
/**
* Create GUI items for GUI Items selection.
* @param parent
*/
private void createTableArea(Composite parent)
{
Composite tableArea = new Composite(parent, SWT.NONE);
tableArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
tableArea.setLayout(new GridLayout(2, false));
Label tableLabel = new Label(tableArea, SWT.NONE);
tableLabel.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 2, 1));
tableLabel.setText(CodeUtilsNLS.ChooseLayoutItemsDialog_GUIItems);
viewer =
new TableViewer(tableArea, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL
| SWT.FULL_SELECTION | SWT.BORDER | SWT.CHECK);
viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
Composite buttonsComposite = new Composite(tableArea, SWT.NONE);
buttonsComposite.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, true, 1, 1));
RowLayout buttonsLayout = new RowLayout(SWT.VERTICAL);
buttonsLayout.pack = false;
buttonsComposite.setLayout(buttonsLayout);
selectAllButton = new Button(buttonsComposite, SWT.PUSH);
selectAllButton.setText(CodeUtilsNLS.UI_SelectAll);
selectAllButton.setLayoutData(new RowData());
selectAllButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
checkAllItems(true);
validate();
}
});
unselectAllButton = new Button(buttonsComposite, SWT.PUSH);
unselectAllButton.setText(CodeUtilsNLS.UI_UnselectAll);
unselectAllButton.setLayoutData(new RowData());
unselectAllButton.addSelectionListener(new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
checkAllItems(false);
validate();
}
});
createColumns(viewer);
final Table table = viewer.getTable();
table.setHeaderVisible(true);
table.setLinesVisible(true);
ArrayContentProvider provider = new ArrayContentProvider();
viewer.setContentProvider(provider);
viewer.addDoubleClickListener(new IDoubleClickListener()
{
@Override
public void doubleClick(DoubleClickEvent event)
{
if (event.getSource() instanceof TableViewer)
{
Table tb = ((TableViewer) event.getSource()).getTable();
TableItem[] items = tb.getSelection();
items[0].setChecked(!items[0].getChecked());
itemCheckStateChanged(items[0]);
validate();
}
}
});
viewer.addSelectionChangedListener(new ISelectionChangedListener()
{
@Override
public void selectionChanged(SelectionChangedEvent event)
{
validate();
}
});
populateViewer();
}
/**
* Notify when the check state changed. This is a workaround for SWT not notifying when we change widget state programatically
*
* @param item
*/
protected void itemCheckStateChanged(TableItem item)
{
//default implementation does nothing
};
private void checkAllItems(boolean checked)
{
for (TableItem item : viewer.getTable().getItems())
{
item.setChecked(checked);
itemCheckStateChanged(item);
}
}
/**
* Creates the columns for the GUI Items table.
* @param viewer the TableViewer whose columns will be created.
*/
protected void createColumns(final TableViewer viewer)
{
String[] titles =
{
CodeUtilsNLS.ChooseLayoutItemsDialog_Id,
CodeUtilsNLS.ChooseLayoutItemsDialog_Type,
CodeUtilsNLS.ChooseLayoutItemsDialog_VariableName,
};
int[] bounds =
{
150, 100, 170
};
TableViewerColumn col = createTableViewerColumn(viewer, titles[0], bounds[0], 0);
col.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
LayoutNode node = (LayoutNode) element;
return node.getNodeId();
}
});
col = createTableViewerColumn(viewer, titles[1], bounds[1], 1);
col.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
LayoutNode node = (LayoutNode) element;
return node.getNodeType();
}
});
col = createTableViewerColumn(viewer, titles[2], bounds[2], 2);
col.setLabelProvider(new ColumnLabelProvider()
{
@Override
public String getText(Object element)
{
LayoutNode node = (LayoutNode) element;
return node.getNodeId();
}
});
}
/**
* Creates a column for the GUI Items table.
* @param parent
*/
protected final TableViewerColumn createTableViewerColumn(TableViewer viewer, String title,
int bound, final int colNumber)
{
final TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
final TableColumn column = viewerColumn.getColumn();
column.setText(title);
column.setWidth(bound);
column.setResizable(true);
column.setMoveable(true);
return viewerColumn;
}
/**
* Populate the combobox that holds projects, with information gathered from the ResourcesPlugin.
* also selects the project set in the init method
*/
private void populateProjects()
{
if (projectNameComboBox != null)
{
IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
int i = 0, selectedProjectIndex = -1;
for (IProject prj : projects)
{
try
{
if (prj.hasNature(IAndroidConstants.ANDROID_NATURE))
{
projectNameComboBox.add(prj.getName());
projectNameComboBox.setData(prj.getName(), prj);
if ((javaProject != null) && prj.equals(javaProject))
{
selectedProjectIndex = i;
}
i++;
}
}
catch (CoreException e)
{
StudioLogger.info(CodeUtilsNLS.Info_ChooseLayoutItemsDialog_Project_Nature);
}
}
if (projectNameComboBox.getItemCount() > 0)
{
if (selectedProjectIndex == -1)
{
projectNameComboBox.select(0);
setJavaProject((IProject) projectNameComboBox.getData(projectNameComboBox
.getText()));
}
else
{
projectNameComboBox.select(selectedProjectIndex);
}
}
validate();
}
}
/**
* Populate the combobox that holds class names.
*/
private void populateClasses()
{
if (classNameComboBox != null)
{
classNameComboBox.removeAll();
if (javaProject != null)
{
int i = 0, selectedTypeIndex = -1;
try
{
List<IType> availableClasses =
JDTUtils.getAvailableActivities(javaProject, new NullProgressMonitor());
availableClasses.addAll(JDTUtils.getAvailableFragmentsSubclasses(
(IProject) projectNameComboBox.getData(projectNameComboBox.getText()),
new NullProgressMonitor()));
//add the fragments JDTUtils.getAvailableFragments to activityClasses
for (IType availableClass : availableClasses)
{
classNameComboBox.add(availableClass.getFullyQualifiedName());
classNameComboBox.setData(availableClass.getFullyQualifiedName(),
availableClass);
if ((javaFile != null)
&& availableClass.getCompilationUnit().equals(javaFile))
{
selectedTypeIndex = i;
}
i++;
}
if (classNameComboBox.getItemCount() > 0)
{
if (selectedTypeIndex == -1)
{
classNameComboBox.select(0);
setJavaFile(((IType) classNameComboBox.getData(classNameComboBox
.getText())).getCompilationUnit());
}
else
{
classNameComboBox.select(selectedTypeIndex);
}
}
}
catch (JavaModelException e)
{
StudioLogger.info(CodeUtilsNLS.Info_ChooseLayoutItemsDialog_Available_Classes);
}
classNameComboBox.setEnabled(classNameComboBox.getItemCount() > 0);
}
}
validate();
}
/**
* Populate the combobox that holds layout file names.
*/
private void populateLayouts()
{
if (layoutFileNameLabel != null)
{
layoutFileNameLabel.setText(DEFAULT_LAYOUT_FILE_NAME_LABEL_VALUE);
layoutFileErrorMessage = null;
IType activity = (IType) classNameComboBox.getData(classNameComboBox.getText());
if (activity != null)
{
try
{
codeGeneratorData = JDTUtils.createLayoutFile(getJavaProject(), getJavaFile());
JavaLayoutData viewLayoutData = getCodeGeneratorData().getJavaLayoutData();
if ((viewLayoutData == null) || (viewLayoutData.hasErrorInCompilationUnitAst()))
{
//there are errors in the compilation unit
layoutFileErrorMessage =
CodeUtilsNLS.ChooseLayoutItemsDialog_TryToGenerateCodeWhenThereIsAnError;
codeGeneratorData = null;
}
else
{
if (getCodeGeneratorData().getLayoutFile().getName() != null)
{
layoutFileNameLabel.setText(getCodeGeneratorData().getLayoutFile()
.getName());
}
else
{
layoutFileErrorMessage =
CodeUtilsNLS.UI_ChooseLayoutItemsDialog_Error_onCreate_Not_Declared;
}
}
}
catch (AndroidException e)
{
//if layout xml is malformed indicate it on screen
layoutFileErrorMessage = e.getMessage();
StudioLogger.error(this.getClass(), "Error parsing layout: " + e.getMessage()); //$NON-NLS-1$
}
populateViewer();
}
validate();
}
}
protected void populateViewer()
{
if (viewer != null)
{
viewer.getTable().removeAll();
// Get the content for the viewer, setInput will call getElements in the
// contentProvider
if (getCodeGeneratorData() != null)
{
viewer.setInput(getGuiItemsList());
viewer.refresh();
}
if (viewer.getTable().getItemCount() == 0)
{
viewer.getTable().setEnabled(false);
selectAllButton.setEnabled(false);
unselectAllButton.setEnabled(false);
}
else
{
selectAllButton.setEnabled(true);
unselectAllButton.setEnabled(true);
viewer.getTable().setEnabled(true);
}
validate();
}
}
/**
* Get the list of items to be displayed
* @return
*/
protected List<LayoutNode> getGuiItemsList()
{
return getCodeGeneratorData().getGUIItems(false);
}
@Override
protected void configureShell(Shell newShell)
{
super.configureShell(newShell);
newShell.setText(shellTitle);
}
/**
* Validate the UI
* @return
*/
protected void validate()
{
String errorMessage = null;
if ((projectNameComboBox != null) && (projectNameComboBox.getItemCount() == 0))
{
errorMessage = CodeUtilsNLS.AbstractLayoutItemsDialog_Error_No_Projects_Found;
}
if ((errorMessage == null) && (classNameComboBox != null)
&& (classNameComboBox.getItemCount() == 0))
{
errorMessage = CodeUtilsNLS.AbstractLayoutItemsDialog_Error_No_Class_Found;
}
if ((errorMessage == null) && (layoutFileNameLabel != null))
{
if (layoutFileErrorMessage != null)
{
errorMessage = layoutFileErrorMessage;
}
else if (getJavaFile() == null)
{
errorMessage = CodeUtilsNLS.AbstractLayoutItemsDialog_Error_No_Layout_Found;
}
}
setErrorMessage(errorMessage);
if (errorMessage == null)
{
setMessage(defaultMessage);
}
if (getButton(OK) != null)
{
getButton(OK).setEnabled((getErrorMessage() == null) && hasAtLeastOneItemChecked());
}
}
private boolean hasAtLeastOneItemChecked()
{
boolean hasItemsChecked = false;
TableItem[] items = viewer.getTable().getItems();
int i = 0;
while (!hasItemsChecked && (i < items.length))
{
if (items[i++].getChecked())
{
hasItemsChecked = true;
}
}
return hasItemsChecked;
}
/**
* @return {@link ICompilationUnit} selected Android file to generate the code
*/
public ICompilationUnit getJavaFile()
{
return javaFile;
}
/**
* @return {@link IProject} of the selected Android file
*/
public IProject getJavaProject()
{
return javaProject;
}
/**
* @return {@link JavaModifierBasedOnLayout} responsible to change the source code of Android file
*/
public JavaModifierBasedOnLayout getModifier()
{
return modifier;
}
private void setModifier(JavaModifierBasedOnLayout modifier)
{
this.modifier = modifier;
}
private void setJavaFile(IFile javaFile)
{
if (javaFile != null)
{
setJavaFile(JavaCore.createCompilationUnitFrom(javaFile));
}
}
private void setJavaFile(ICompilationUnit javaFile)
{
this.javaFile = javaFile;
setJavaProject(javaFile.getJavaProject().getProject());
}
private void setJavaProject(IProject javaProject)
{
this.javaProject = javaProject;
}
/**
* @return object representing the input data (from layout XML) to use as basis during code generation
*/
public CodeGeneratorDataBasedOnLayout getCodeGeneratorData()
{
return codeGeneratorData;
}
public void setHelpID(String helpID)
{
this.helpID = helpID;
}
/**
* @return table to select the Android GUI items to generate code for
*/
public TableViewer getViewer()
{
return viewer;
}
}