Java tutorial
/************************************************************************** * ERA - Eclipse Requirements Analysis * ============================================== * Copyright (C) 2009-2013 by Georg Blaschke, Christoph P. Neumann * and Bernd Haberstumpf (http://era.origo.ethz.ch) ************************************************************************** * Licensed under the Eclipse Public License - v 1.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.eclipse.org/org/documents/epl-v10.html * 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 era.foss.objecteditor.specobject; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.UpdateValueStrategy; import org.eclipse.core.databinding.beans.PojoObservables; import org.eclipse.core.databinding.observable.ChangeEvent; import org.eclipse.core.databinding.observable.IChangeListener; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.observable.value.IValueChangeListener; import org.eclipse.core.databinding.observable.value.ValueChangeEvent; import org.eclipse.emf.databinding.EMFProperties; import org.eclipse.emf.databinding.IEMFListProperty; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.jface.databinding.viewers.IViewerObservableValue; import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider; import org.eclipse.jface.databinding.viewers.ViewerProperties; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.jface.viewers.IInputSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.nebula.widgets.compositetable.CompositeTable; import org.eclipse.swt.nebula.widgets.compositetable.IRowContentProvider; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import era.foss.erf.ERF; import era.foss.erf.EraToolExtension; import era.foss.erf.ErfPackage; import era.foss.erf.SpecObject; import era.foss.erf.SpecType; import era.foss.erf.ToolExtension; import era.foss.erf.contrib.HierachicalSpecObjectProvider; import era.foss.objecteditor.contrib.IAllowViewerSchemaChange; import era.foss.ui.contrib.NotifyingListSizeProperty; /** * The Class SpecObjectCompositeViewer. */ public class SpecObjectCompositeViewer extends Viewer implements IInputSelectionProvider, IAllowViewerSchemaChange { /** Master observable referring to the currently selected view. */ IViewerObservableValue viewMaster; /** ERF Model. */ ERF erfModel; /** ERA specific extensions. */ EraToolExtension toolExtension; /** top level composite of this viewer. */ Composite topLevelComposite; /** composite table showing the spec objects. */ CompositeTable compositeTable; /** Button bar for various buttons (adding elements, selecting views,... */ Composite buttonBarComposite; /** editing Domain. */ EditingDomain editingDomain; /** Databinding context for this viewer. */ DataBindingContext dbc; /** The current selected SpecObjects. */ protected LinkedHashMap<Integer, SpecObject> selectedSpecObjectMap = new LinkedHashMap<Integer, SpecObject>(); /** List of selection changed listeners of this viewer. */ List<ISelectionChangedListener> selectionChangedListeners = new LinkedList<ISelectionChangedListener>(); HierachicalSpecObjectProvider specObjectProvider; /** The spec type master. */ protected IObservableValue specTypeMaster; /** * Create a Viewer for the SpecObjects. * * @param parent the parent * @param editingDomain the editing domain * @param erfModel the erf model */ public SpecObjectCompositeViewer(Composite parent, AdapterFactoryEditingDomain editingDomain, ERF erfModel) { this.editingDomain = editingDomain; this.erfModel = erfModel; this.dbc = new DataBindingContext(); // find Era specific tool extensions for (ToolExtension toolExtension : this.erfModel.getToolExtensions()) { if (toolExtension.eClass().getClassifierID() == ErfPackage.ERA_TOOL_EXTENSION) { this.toolExtension = (EraToolExtension) toolExtension; } } assert (this.toolExtension != null); // TODO: don't rely on the existence of a specification specObjectProvider = new HierachicalSpecObjectProvider( this.erfModel.getCoreContent().getSpecifications().get(0)); doLayout(parent); // binding the UI with the model binding(); } /** * layout the viewer gui elements * * @param parent */ void doLayout(Composite parent) { topLevelComposite = new Composite(parent, SWT.NONE); topLevelComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); topLevelComposite.setLayout(new GridLayout(1, false)); // button bar createButtonBar(); createCompositeTable(); } /** * Create the composite table */ private void createCompositeTable() { // composite table this.compositeTable = new CompositeTable(topLevelComposite, SWT.NULL | SWT.NO_SCROLL); this.compositeTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 0, 0)); SpecObjectViewerRow.setViewMaster(viewMaster); SpecObjectViewerRow.setEditingDomain(editingDomain); SpecObjectViewerRow.setErfModel(erfModel); new SpecObjectViewerRow(compositeTable, SWT.NULL); compositeTable.setRunTime(true); } /* * (non-Javadoc) * * @see org.eclipse.jface.viewers.Viewer#getControl() */ @Override public Control getControl() { return topLevelComposite; } /* * (non-Javadoc) * * @see org.eclipse.jface.viewers.Viewer#refresh() */ @Override public void refresh() { compositeTable.refreshAllRows(); } @Override public void recreateViewerSchema() { dbc.dispose(); viewMaster.dispose(); buttonBarComposite.dispose(); createButtonBar(); compositeTable.dispose(); createCompositeTable(); binding(); topLevelComposite.layout(); // buttonBarComposite.layout(); // viewerComposite.update(); } /* * (non-Javadoc) * * @see org.eclipse.jface.viewers.Viewer#getInput() */ @Override public Object getInput() { // TODO implement getInput(..) which stems from IInputProvider return null; } /* * (non-Javadoc) * * @see org.eclipse.jface.viewers.Viewer#setInput(java.lang.Object) */ @Override public void setInput(Object input) { // TODO implement setInput(..) } /** * A row of the composite table */ /** * Create button bar showing: * <ul> * <li>combo box for the view</li> * <li>button for adding new SpecObjects</li> * </ul> * * * @param viewerComposite the parent composite * @return */ private void createButtonBar() { buttonBarComposite = new Composite(topLevelComposite, SWT.NONE); buttonBarComposite.setLayout(new GridLayout(4, true)); buttonBarComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 0, 0)); /* * create combo box showing the availible views */ ComboViewer viewComboViewer = new ComboViewer(buttonBarComposite, SWT.READ_ONLY) { @Override protected void doUpdateItem(Widget data, Object element, boolean fullMap) { // memorize the selection before updating the item, as the // update routine removes the selection... ISelection currentSelection = this.getSelection(); super.doUpdateItem(data, element, fullMap); // set the selection to the previous value this.setSelection(currentSelection); } }; ObservableListContentProvider contentProvider = new ObservableListContentProvider(); viewComboViewer.setContentProvider(contentProvider); viewComboViewer.setLabelProvider( new ObservableMapLabelProvider(EMFProperties.value(ErfPackage.Literals.IDENTIFIABLE__LONG_NAME) .observeDetail(contentProvider.getKnownElements()))); IEMFListProperty dataTypeDefinitions = EMFProperties.list(ErfPackage.Literals.ERA_TOOL_EXTENSION__VIEWS); IObservableList observableList = dataTypeDefinitions.observe(toolExtension); viewComboViewer.setInput(observableList); // use first view available // TODO: use a dedicated default view if available if (toolExtension.getViews().size() > 0) { viewComboViewer.setSelection(new StructuredSelection(toolExtension.getViews().get(0))); } viewComboViewer.getControl().setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 1)); viewMaster = ViewerProperties.singleSelection().observe(viewComboViewer); // refresh composite table in case view has been changed viewMaster.addChangeListener(new IChangeListener() { @Override public void handleChange(ChangeEvent event) { dbc.dispose(); compositeTable.dispose(); createCompositeTable(); binding(); topLevelComposite.layout(); } }); /* * Create Combo box for selecting the SpecType */ final ComboViewer specTypecomboViewer = new ComboViewer(buttonBarComposite, SWT.READ_ONLY) { @Override protected void doUpdateItem(Widget data, Object element, boolean fullMap) { // memorize the selection before updating the item, as the // update routine removes the selection... ISelection currentSelection = this.getSelection(); super.doUpdateItem(data, element, fullMap); // set the selection to the previous value this.setSelection(currentSelection); } }; ObservableListContentProvider comboViewercontentProvider = new ObservableListContentProvider(); specTypecomboViewer.getControl().setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false)); // set content provider specTypecomboViewer.setContentProvider(comboViewercontentProvider); // set label provider specTypecomboViewer.setLabelProvider( new ObservableMapLabelProvider(EMFProperties.value(ErfPackage.Literals.IDENTIFIABLE__LONG_NAME) .observeDetail(comboViewercontentProvider.getKnownElements()))); // set input IEMFListProperty specTypeProperty = EMFProperties.list(ErfPackage.Literals.CONTENT__SPEC_TYPES); specTypecomboViewer.setInput(specTypeProperty.observe(this.erfModel.getCoreContent())); // TODO: use a dedicated default type if available if (erfModel.getCoreContent().getSpecTypes().size() > 0) { specTypecomboViewer .setSelection(new StructuredSelection(erfModel.getCoreContent().getSpecTypes().get(0))); } specTypeMaster = ViewerProperties.singleSelection().observe(specTypecomboViewer); /* * create add button */ final Button addButton = new Button(buttonBarComposite, SWT.NONE); addButton.setImage(PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD)); addButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false)); addButton.setEnabled(specTypeMaster.getValue() != null); addButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { SpecObjectHandler.createNewSpecObject(editingDomain, erfModel.getCoreContent(), (SpecType) SpecObjectCompositeViewer.this.specTypeMaster.getValue(), erfModel.getCoreContent().getSpecifications().get(0)); } }); specTypeMaster.addValueChangeListener(new IValueChangeListener() { @Override public void handleValueChange(ValueChangeEvent event) { addButton.setEnabled(event.getObservableValue().getValue() != null); } }); } /** * Bind composite table and rows of composite table to data model. Also handle selection events from the composite * table */ private void binding() { dbc.bindValue(PojoObservables.observeValue(compositeTable, "numRowsInCollection"), new NotifyingListSizeProperty().observe(specObjectProvider.getSpecObjectList()), new UpdateValueStrategy(UpdateValueStrategy.POLICY_NEVER), new UpdateValueStrategy()); compositeTable.addRowContentProvider(new SpecObjectRowContentProvider()); } @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { if (!selectionChangedListeners.contains(listener)) { selectionChangedListeners.add(listener); } } @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { selectionChangedListeners.remove(listener); } /* * (non-Javadoc) * * @see org.eclipse.jface.viewers.Viewer#getSelection() */ @Override public ISelection getSelection() { if (selectedSpecObjectMap.size() == 0) { return StructuredSelection.EMPTY; } return new StructuredSelection(selectedSpecObjectMap.values().toArray()); } @SuppressWarnings("unchecked") @Override public void setSelection(ISelection selection, boolean reveal) { if (selection.isEmpty()) { return; } // unpack ISelection into SpecObject assert (selection instanceof StructuredSelection); // remove all Spec Objects selectedSpecObjectMap.clear(); Integer selectedSpecOjectOffset = null; // set selectedSpecObject to given one for (SpecObject specObject : ((List<SpecObject>) ((StructuredSelection) selection).toList())) { int specOjectOffset = specObjectProvider.getSpecObjectList().indexOf(specObject); if (selectedSpecOjectOffset == null) { selectedSpecOjectOffset = specOjectOffset; } selectedSpecObjectMap.put(specOjectOffset, specObject); } // if specObject is not yet displayed in the composite table show it in the first row if (selectedSpecOjectOffset < compositeTable.getTopRow() || selectedSpecOjectOffset > (compositeTable.getTopRow() + compositeTable.getNumRowsVisible())) { compositeTable.setTopRow(selectedSpecOjectOffset); updateRowSelectionStatus(true); } else { updateRowSelectionStatus(false); } } /** * Update the selection status of all rows controls */ private void updateRowSelectionStatus(boolean setFocus) { for (Control control : compositeTable.getRowControls()) { SpecObjectViewerRow row = (SpecObjectViewerRow) control; row.setSelected(selectedSpecObjectMap.containsKey(row.getSpecObjectOffset()), setFocus); } // send event to selectionChangedListener SelectionChangedEvent selectionChangeEvent = new SelectionChangedEvent(SpecObjectCompositeViewer.this, new StructuredSelection(selectedSpecObjectMap.values().toArray())); for (ISelectionChangedListener listener : selectionChangedListeners) { listener.selectionChanged(selectionChangeEvent); } } /** * Content Provider for Row in the Composite table. */ private class SpecObjectRowContentProvider implements IRowContentProvider { /** * Keeps a reference of each delete listener for each row, so we can remove a listener when compositetable * associate the row to another SpecObject. */ private Map<SpecObjectViewerRow, MouseListener> deleteListenerMap = new HashMap<SpecObjectViewerRow, MouseListener>(); /** * Keeps a reference of each selection listener for each row, so we can remove a listener when compositetable * associate the row to another SpecObject. */ private Map<SpecObjectViewerRow, SelectionListener> selectionListenerMap = new HashMap<SpecObjectViewerRow, SelectionListener>(); /** * remove and add selectionlistener bind new SpecObject to Row */ public void refresh(CompositeTable sender, final int currentObjectOffset, Control rowControl) { final SpecObjectViewerRow currentRow = (SpecObjectViewerRow) rowControl; final SpecObject currentSpecObject = specObjectProvider.getSpecObjectList().get(currentObjectOffset); // set offset of the current specObject currentRow.setSpecObject(currentSpecObject, currentObjectOffset); // bind the new SpecObject currentRow.bind(currentSpecObject); // in case the specObject is selected set the selected status of the row currentRow.setSelected(selectedSpecObjectMap.containsKey(currentObjectOffset), false); // listener for the delete button of the row MouseListener deleteListener = new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { SpecObjectHandler.deleteSpecObject(editingDomain, currentSpecObject); } }; // listener for selection events of a row SelectionListener selectionListener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // SHIFT is not pressed: set selection to the SpecObject associated with the current row if ((e.stateMask & SWT.SHIFT) == 0 || SpecObjectCompositeViewer.this.selectedSpecObjectMap.isEmpty()) { SpecObjectCompositeViewer.this.selectedSpecObjectMap.clear(); SpecObjectCompositeViewer.this.selectedSpecObjectMap.put(currentObjectOffset, currentSpecObject); } // SHIFT is pressed: add all elements between the selected SpecObject and the SpecObject // associated with the current row else { int selectedSpecObjectOffset = (Integer) selectedSpecObjectMap.keySet().toArray()[0]; int objectOffset = Math.min(currentObjectOffset, selectedSpecObjectOffset); int objectEndOffset = Math.max(currentObjectOffset, selectedSpecObjectOffset); while (objectOffset <= objectEndOffset) { SpecObjectCompositeViewer.this.selectedSpecObjectMap.put(objectOffset, specObjectProvider.getSpecObjectList().get(objectOffset)); objectOffset++; } } // Only set the focus in case the event is sent by a composite boolean setFocus = e.widget instanceof Composite; // update the selection status of the row controls SpecObjectCompositeViewer.this.updateRowSelectionStatus(setFocus); } }; // add listener to row currentRow.addSelectionListener(selectionListener); // keep a reference to the listeners to be able to remove it // during the next refresh deleteListenerMap.put(currentRow, deleteListener); selectionListenerMap.put(currentRow, selectionListener); } } }