/************************************************************************** * 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.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.databinding.viewers.IViewerObservableValue; 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.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; 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.Label; import era.foss.erf.AttributeDefinition; import era.foss.erf.AttributeValue; import era.foss.erf.DatatypeDefinition; import era.foss.erf.ERF; import era.foss.erf.ErfPackage; import era.foss.erf.SpecObject; import era.foss.erf.SpecType; import era.foss.erf.View; import era.foss.erf.ViewElement; import era.foss.ui.contrib.PageBookMax; import era.foss.util.types.Tuple; /** * The Class SpecObjectViewerRow. */ class SpecObjectViewerRow extends Composite { /** Master observable for the chosen view. */ static private IViewerObservableValue viewMaster; /** The editing domain used for modifications on the model. */ static private EditingDomain editingDomain; /** The model. */ private static ERF erfModel; /** The composite holding the all the controls according to the chosen view. */ PageBookMax pageBook; /** List of Composites holding the graphical representation of a single AttributeDefinition. */ private final List<AbstractAttributeDefinitionComposite> attributeDefintionCompositeList = new ArrayList<AbstractAttributeDefinitionComposite>(); /** offset of the SpecObject associated with this row. */ private int specObjectOffset; /** List of selection listeners of this row. */ private List<SelectionListener> selectionListenerList = new LinkedList<SelectionListener>(); /** The spec object. */ private SpecObject specObject; /** The page map. */ private HashMap<SpecType, Composite> pageMap = new HashMap<SpecType, Composite>(); /** listner for adding and removing AttributeValue of SpecObjects */ private AttributeValueAddRemoveListener attributeValueAddRemoveListener; /** * Updates the composites in case of an AttributeValue is added or removed from a SpecObject (e.g. by Undo or Redo * actions) * * In case a an AttributeValue is added or removed then the respective composite is bound to a the new * AttributeValue in case there is any */ private class AttributeValueAddRemoveListener extends AdapterImpl { @Override public void notifyChanged( Notification msg ) { super.notifyChanged( msg ); Object changedValue = null; // find changed object for determining the attribute definition if( (msg.getEventType() == Notification.REMOVE) ) { changedValue = msg.getOldValue(); } else if( (msg.getEventType() == Notification.ADD) ) { changedValue = msg.getNewValue(); } if( (changedValue != null) && (changedValue instanceof AttributeValue) ) { AttributeDefinition attributeDefinition = ((AttributeValue)changedValue).getDefinition(); // find composites to re-bind for( AbstractAttributeDefinitionComposite attributeDefinitionComposite : attributeDefintionCompositeList ) { if( attributeDefinitionComposite.getViewElement().getAttributeDefinition() == attributeDefinition ) { attributeDefinitionComposite.bind( (SpecObject)msg.getNotifier(), (AttributeValue)msg.getNewValue(), editingDomain ); } } } } }; /** * Constructor of the Row Composite. * * @param parent the parent * @param style the style */ public SpecObjectViewerRow( Composite parent, int style ) { super( parent, style ); // layout button and controls this.setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true ) ); this.setLayout( new GridLayout( 2, false ) ); createDeleteButton( this ); // Use PageBook as a top level composite for the row // A PageBook can show different pages for different SpecObjectTypes pageBook = new PageBookMax( this, SWT.NONE ); pageBook.setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true ) ); // viewComposite.setLayout( new StackLayout() ); // create the controls for each SpecType for( SpecType specType : erfModel.getCoreContent().getSpecTypes() ) { createSpecObjectControls( pageBook, specType ); } // add menu for all elements createContextMenu(); createSelectionListener(); } /** * Creates the context menu for a SpecObject * * This function registers menu for all gui elements the in the row. Reason: There are different cascaded controls * which are should react on a mouse click. */ protected void createContextMenu() { MenuManager menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown( true ); menuMgr.addMenuListener( new IMenuListener() { public void menuAboutToShow( IMenuManager manager ) { SpecObjectHandler.createCommonMenuItems( manager, editingDomain, specObject ); } } ); // register menu for all gui elements the in row this.createContextMenuComposite( menuMgr, this ); } /** * Create the context menu for one composite and iterate over its children * * @param menuMgr * @param control */ private void createContextMenuComposite( MenuManager menuMgr, Control control ) { if( (control.getMenu() == null) ) { control.setMenu( menuMgr.createContextMenu( control ) ); } // add menu to all children if( control instanceof Composite ) { for( Control childControl : ((Composite)control).getChildren() ) { this.createContextMenuComposite( menuMgr, childControl ); } } } /** * Set the master of the view to observe. * * @param viewMaster master observable */ public static void setViewMaster( IViewerObservableValue viewMaster ) { SpecObjectViewerRow.viewMaster = viewMaster; } /** * Set the editing domain used for operations on an AttributeValue of the associated SpecObject. * * @param editingDomain the new editing domain */ public static void setEditingDomain( EditingDomain editingDomain ) { SpecObjectViewerRow.editingDomain = editingDomain; } /** * Set the editing domain used for operations on an AttributeValue of the associated SpecObject. * * @param erfModel the new erf model */ public static void setErfModel( ERF erfModel ) { SpecObjectViewerRow.erfModel = erfModel; } /** * Sort the elements in the view according to row number and position in row * * @return */ private ViewElement[][] createViewElementMatrix( SpecType specType ) { ViewElement[][] viewElementMatrix; List<ViewElement> viewElementList = new ArrayList<ViewElement>(); // get view View selectedView = (View)viewMaster.getValue(); if( selectedView == null ) { return null; } // find all view elements referring to the current specObject for( ViewElement viewElement : selectedView.getViewElements() ) { if( viewElement.getAttributeDefinition() != null && viewElement.getAttributeDefinition().getSpecType().equals( specType ) ) { viewElementList.add( viewElement ); } } // check if there are elements to be shown in the selected view if( viewElementList.size() == 0 ) { return null; } // calculate maxColumnSpan Tuple<Integer, Integer> dimension = getDimensions( viewElementList ); int numColumns = dimension.x; int numRows = dimension.y; viewElementMatrix = new ViewElement[numRows][numColumns]; for( final ViewElement viewElement : viewElementList ) { final int rowPos = viewElement.getEditorRowPosition(); final int rowSpan = viewElement.getEditorRowSpan(); final int columnSpan = viewElement.getEditorColumnSpan(); final int columnPos = viewElement.getEditorColumnPosition(); for( int rowPosSpan = rowPos; rowPosSpan < (rowPos + rowSpan); rowPosSpan++ ) { for( int columnPosSpan = columnPos; columnPosSpan < (columnPos + columnSpan); columnPosSpan++ ) { viewElementMatrix[rowPosSpan][columnPosSpan] = viewElement; } } } return viewElementMatrix; } /** * create the delete button for the row */ private void createDeleteButton( Composite parent ) { } /** * create controls for elements in the chosen view */ private void createSpecObjectControls( Composite parent, SpecType specType ) { // create matrix with holding references to ViewElements ViewElement viewElementMatrix[][] = createViewElementMatrix( specType );; // Only create controls in case there are elements to show if( viewElementMatrix == null ) { return; } int numRows = viewElementMatrix.length; int numColumns = viewElementMatrix[0].length; Composite viewCompositePage = new Composite( parent, SWT.NONE ); // viewCompositePage.setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true ) ); viewCompositePage.setLayout( new GridLayout( numColumns, true ) ); pageMap.put( specType, viewCompositePage ); for( int rowPos = 0; rowPos < numRows; rowPos++ ) { for( int columnPos = 0; columnPos < numColumns; columnPos++ ) { // create padding if no element is occupying this spot if( viewElementMatrix[rowPos][columnPos] == null ) { // determine horizontal span of the padding int paddingSpan = 1; while (((columnPos + paddingSpan) < numColumns) && (viewElementMatrix[rowPos][columnPos + paddingSpan] == null)) { paddingSpan++; } // padding: fill up this line with an empty label Label paddingLabel = new Label( viewCompositePage, SWT.BORDER ); paddingLabel.setText( "Pad" ); paddingLabel.setVisible( false ); paddingLabel.setLayoutData( new GridData( SWT.FILL, SWT.CENTER, false, false, paddingSpan, 1 ) ); columnPos += paddingSpan - 1; } else { ViewElement viewElement = viewElementMatrix[rowPos][columnPos]; // if this spot is occupied by the origin coordinates of an viewElement create the respective // control if( viewElement.getAttributeDefinition() != null && rowPos == viewElement.getEditorRowPosition() && columnPos == viewElement.getEditorColumnPosition() ) { int controlColumnSpan = viewElement.getEditorColumnSpan(); // show label (only if there is space for a label if( (viewElement.isEditorShowLabel() == true) && (controlColumnSpan > 1) ) { Label label = new Label( viewCompositePage, SWT.NULL ); label.setText( viewElement.getAttributeDefinition().getLongName() + ":" ); label.setLayoutData( new GridData( SWT.RIGHT, SWT.BEGINNING, false, false, 1, viewElement.getEditorRowSpan() ) ); controlColumnSpan--; } AbstractAttributeDefinitionComposite control = null; DatatypeDefinition dataTypeDefinition = viewElement.getAttributeDefinition().getType(); if( dataTypeDefinition != null ) { switch (dataTypeDefinition.eClass().getClassifierID()) { case ErfPackage.DATATYPE_DEFINITION_INTEGER: case ErfPackage.DATATYPE_DEFINITION_STRING: control = new AttributeDefinitionStringComposite( viewCompositePage, viewElement, specObject ); break; case ErfPackage.DATATYPE_DEFINITION_BOOLEAN: control = new AttributeDefinitionBooleanComposite( viewCompositePage, viewElement, specObject ); break; case ErfPackage.DATATYPE_DEFINITION_ENUMERATION: control = new AttributeDefinitionEnumComposite( viewCompositePage, viewElement, specObject ); break; } } if( control != null ) { GridData gd = new GridData( SWT.FILL, SWT.FILL, true, true, controlColumnSpan, viewElement.getEditorRowSpan() ); gd.minimumHeight = control.computeSize( SWT.DEFAULT, SWT.DEFAULT ).y * viewElement.getEditorRowSpan(); gd.heightHint = control.computeSize( SWT.DEFAULT, SWT.DEFAULT ).y * viewElement.getEditorRowSpan(); control.setLayoutData( gd ); attributeDefintionCompositeList.add( control ); } } } } } } /** * Get the maximum number of columns needed for the GridLayout * * @return maximum Column span */ private Tuple<Integer, Integer> getDimensions( final List<ViewElement> viewElementList ) { // the number of columns int columnPosMax = 0; // the number of rows int rowPosMax = 0; for( ViewElement viewElement : viewElementList ) { // get the last column position used by this element int columnPos = viewElement.getEditorColumnPosition() + viewElement.getEditorColumnSpan(); columnPosMax = Math.max( columnPos, columnPosMax ); // get the last row position used by this element int rowPos = viewElement.getEditorRowPosition() + viewElement.getEditorRowSpan(); rowPosMax = Math.max( rowPos, rowPosMax ); } return new Tuple<Integer, Integer>( columnPosMax, rowPosMax ); } /** * Binds the current SpecObject. * * @param specObject the spec object */ public void bind( SpecObject specObject ) { // add listener to the spec obejct which takes care about changes in list of AttributeValue if( this.attributeValueAddRemoveListener == null ) { this.attributeValueAddRemoveListener = new AttributeValueAddRemoveListener(); } else { attributeValueAddRemoveListener.getTarget().eAdapters().remove( attributeValueAddRemoveListener ); } specObject.eAdapters().add( attributeValueAddRemoveListener ); // create Hashmap for look up the attribute values for a certain attribute definition HashMap<AttributeDefinition, AttributeValue> attributeDefintionValueMap = new HashMap<AttributeDefinition, AttributeValue>(); for( AttributeValue attributeValue : specObject.getValues() ) { attributeDefintionValueMap.put( attributeValue.getDefinition(), attributeValue ); } // bind the controls for( AbstractAttributeDefinitionComposite attributeDefinitionComposite : attributeDefintionCompositeList ) { attributeDefinitionComposite.bind( specObject, attributeDefintionValueMap.get( attributeDefinitionComposite.getViewElement() .getAttributeDefinition() ), editingDomain ); } } /** * Set the selection status of this row. Alter the background when the row gets selected. * * @param isSelected if true the row is displayed as selected * @param setFocus the set focus */ public void setSelected( boolean isSelected, boolean setFocus ) { if( isSelected ) { this.setBackground( Display.getDefault().getSystemColor( SWT.COLOR_LIST_SELECTION ) ); // set focus to the first element in the view if( setFocus ) { if( attributeDefintionCompositeList.size() > 0 ) { attributeDefintionCompositeList.get( 0 ).getControl().setFocus(); } } } else { this.setBackground( Display.getDefault().getSystemColor( SWT.COLOR_WIDGET_BACKGROUND ) ); } } /** * retrieve all child controls of a given composite * * @param composite the composite to get the childs from * @param controlList the list where the controls are added to */ private void getAllChildControls( Composite composite, List<Control> controlList ) { for( Control control : composite.getChildren() ) { controlList.add( control ); if( control instanceof Composite ) { getAllChildControls( (Composite)control, controlList ); } } } /** * Add a selection listener to this row. * * @param listener the listener */ public void addSelectionListener( SelectionListener listener ) { this.selectionListenerList.add( listener ); } /** * Remove a selection listener. * * @param listener the listener */ public void removeSelectionListener( SelectionListener listener ) { this.selectionListenerList.remove( listener ); } /** * create a wrapper for the mouse listener attached to all controls of the row */ private void createSelectionListener() { MouseListener mouseListener = new MouseAdapter() { @Override public void mouseDown( MouseEvent mouseEvent ) { super.mouseDown( mouseEvent ); Event event = new Event(); event.widget = mouseEvent.widget; event.stateMask = mouseEvent.stateMask; SelectionEvent selectionEvent = new SelectionEvent( event ); for( SelectionListener selectionListener : selectionListenerList ) { selectionListener.widgetSelected( selectionEvent ); } } }; this.addMouseListener( mouseListener ); List<Control> controlList = new ArrayList<Control>(); getAllChildControls( this, controlList ); for( Control control : controlList ) { control.addMouseListener( mouseListener ); } } /** * set the {@link SpecObject} and offset of this {@link SpecObject} associated with this row * * @param specObject the s{@link SpecObject} show in this row * @param offset of the SpecObject */ public void setSpecObject( SpecObject specObject, int offset ) { this.specObjectOffset = offset; this.specObject = specObject; // show page according to specType if( specObject != null && specObject.getType() != null ) { Composite page = pageMap.get( specObject.getType() ); if( page != null ) { pageBook.showPage( page ); } } } /** * get the offset of the SpecObject associated with this row. * * @return the offset of the SpecObject */ public int getSpecObjectOffset() { return this.specObjectOffset; } }