// Copyright (c) 2006-2008 by Leif Frenzel - see http://leiffrenzel.de // This code is made available under the terms of the Eclipse Public License, // version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html package net.sf.eclipsefp.haskell.ui.internal.preferences.hsimpls; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.sf.eclipsefp.haskell.core.compiler.CompilerManager; import net.sf.eclipsefp.haskell.core.compiler.HsImplementation; import net.sf.eclipsefp.haskell.core.compiler.HsImplementationPersister; import net.sf.eclipsefp.haskell.core.compiler.IHsImplementation; import net.sf.eclipsefp.haskell.ui.internal.util.UITexts; import net.sf.eclipsefp.haskell.ui.util.SWTUtil; import org.eclipse.core.runtime.ListenerList; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; /** <p>A composite that displays installed Haskell implementations in a * table. Implementations can be added, removed and edited.</p> * * @author Leif Frenzel */ class ImplementationsBlock implements ISelectionProvider { private static final String KEY_COLUMN_WIDTH = ".columnWidth"; //$NON-NLS-1$ private static final String KEY_SORT_COLUMN = ".sortColumn"; //$NON-NLS-1$ private static final int DEFAULT_WIDTH = 100; private final List<IHsImplementation> installations = new ArrayList<>(); private CheckboxTableViewer viewer; private Table table; private Button btnAdd; private Button btnRemove; private Button btnEdit; private Button btnAutoDetect; // index of column used for sorting private int sortColumn = 0; private final ListenerList selectionListeners = new ListenerList(); private ISelection lastSelection = new StructuredSelection(); Composite createControl( final Composite parent ) { Composite composite = SWTUtil.createMainComposite( parent ); Label tableLabel = new Label( composite, SWT.NONE ); tableLabel.setText( UITexts.implementationsBlock_installed ); GridData data = new GridData(); data.horizontalSpan = 2; tableLabel.setLayoutData( data ); tableLabel.setFont( parent.getFont() ); table = SWTUtil.createTable( composite ); table.addKeyListener( new KeyAdapter() { @Override public void keyPressed(final KeyEvent event) { if( event.character == SWT.DEL && event.stateMask == 0 ) { removeSelectedInstallations(); } } } ); createColumns(); createViewer(); Composite buttonsComp = createButtonsComposite( composite ); createButtons( buttonsComp ); createSpacer( buttonsComp ); enableButtons(); return composite; } private void sortByType() { viewer.setComparator( new Comparator_TypeName() ); sortColumn = 3; } private void sortByName() { viewer.setComparator( new Comparator_Name() ); sortColumn = 1; } private void sortByVersion() { viewer.setComparator( new Comparator_Version() ); sortColumn = 2; } void applyPref( final String prefValue ) { HsImplementationPersister.fromXML( prefValue, installations ); viewer.setInput( installations ); viewer.refresh(); } String getPref() { return HsImplementationPersister.toXML( installations ); } public List<IHsImplementation> getInstallations() { return installations; } void add( final HsImplementation impl ) { installations.add( impl ); viewer.setInput( installations ); viewer.refresh(); } boolean isDuplicateName( final String name, final HsImplementation impl ) { return CompilerManager.isDuplicateName( installations, name, impl ); // boolean result = false; // if( name != null && name.trim().length() > 0 ) { // for( IHsImplementation inst: installations ) { // result |= inst!=impl && name.equals( inst.getName() ); // } // } // return result; } void setCheckedHsImplementation( final String name ) { IHsImplementation impl = findImpl( name ); if( impl == null ) { setSelection( new StructuredSelection() ); } else { setSelection( new StructuredSelection( impl ) ); } } private IHsImplementation findImpl( final String name ) { IHsImplementation result = null; for( IHsImplementation impl: installations ) { if( impl.getName().equals( name ) ) { result = impl; } } return result; } IHsImplementation getCheckedHsImplementation() { IHsImplementation result = null; Object[] objects = viewer.getCheckedElements(); if( objects.length > 0 ) { result = ( IHsImplementation )objects[ 0 ]; } return result; } void setInstallations( final IHsImplementation[] insts ) { installations.clear(); for( int i = 0; i < insts.length; i++ ) { installations.add( insts[ i ] ); } viewer.setInput( installations ); viewer.refresh(); } void saveColumnSettings( final IDialogSettings settings, final String qualifier ) { int columnCount = table.getColumnCount(); for( int i = 0; i < columnCount; i++ ) { int width = table.getColumn( i ).getWidth(); settings.put( qualifier + KEY_COLUMN_WIDTH + i, width ); } settings.put( qualifier + KEY_SORT_COLUMN, sortColumn ); } void restoreColumnSettings( final IDialogSettings settings, final String qualifier ) { viewer.getTable().layout( true ); restoreColumnWidths( settings, qualifier ); try { sortColumn = settings.getInt( qualifier + KEY_SORT_COLUMN ); } catch ( final NumberFormatException numfex ) { sortColumn = 1; } switch( sortColumn ) { case 1: sortByName(); break; case 2: sortByVersion(); break; case 3: sortByType(); break; } } // interface methods of ISelectionProvider ////////////////////////////////////////// @Override public void addSelectionChangedListener( final ISelectionChangedListener li ) { selectionListeners.add( li ); } @Override public ISelection getSelection() { return new StructuredSelection( viewer.getCheckedElements() ); } @Override public void removeSelectionChangedListener( final ISelectionChangedListener li ) { selectionListeners.remove( li ); } @Override public void setSelection( final ISelection selection ) { if( selection instanceof IStructuredSelection ) { if( !selection.equals( lastSelection ) ) { lastSelection = selection; Object elem = ( ( IStructuredSelection )selection ).getFirstElement(); if( elem == null ) { viewer.setCheckedElements( new Object[ 0 ] ); } else { viewer.setCheckedElements( new Object[] { elem } ); viewer.reveal( elem ); } fireSelectionChanged(); } } } // helping functions //////////////////// private void fireSelectionChanged() { SelectionChangedEvent evt = new SelectionChangedEvent( this, getSelection() ); Object[] lis = selectionListeners.getListeners(); for( int i = 0; i < lis.length; i++ ) { ISelectionChangedListener li = ( ISelectionChangedListener )lis[ i ]; li.selectionChanged( evt ); } } private void enableButtons() { IStructuredSelection ssel = (IStructuredSelection) viewer.getSelection(); btnEdit.setEnabled( ssel.size() == 1 ); boolean moreThanSelectedItem = ssel.size() > 0 && ssel.size() < viewer.getTable().getItemCount(); btnRemove.setEnabled( moreThanSelectedItem ); } private void createColumns() { createColumn( UITexts.implementationsBlock_colName, new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent evt ) { sortByName(); } } ); createColumn( UITexts.implementationsBlock_colType, new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent evt ) { sortByType(); } } ); createColumn( UITexts.implementationsBlock_colVersion, new SelectionAdapter() { @Override public void widgetSelected( final SelectionEvent evt ) { sortByVersion(); } } ); } private TableColumn createColumn( final String text, final SelectionListener listener ) { TableColumn result = new TableColumn( table, SWT.NONE ); result.setText( text ); result.addSelectionListener( listener ); return result; } private Composite createButtonsComposite( final Composite parent ) { Composite result = new Composite( parent, SWT.NULL ); result.setLayoutData( new GridData( GridData.VERTICAL_ALIGN_BEGINNING ) ); GridLayout gridLayout = new GridLayout(); gridLayout.marginHeight = 0; gridLayout.marginWidth = 0; result.setLayout( gridLayout ); result.setFont( parent.getFont() ); return result; } private void createButtons( final Composite buttonsComp ) { String sAdd = UITexts.implementationsBlock_btnAdd; btnAdd = SWTUtil.createPushButton( buttonsComp, sAdd ); btnAdd.addListener( SWT.Selection, new Listener() { @Override public void handleEvent( final Event evt ) { addHsImplementation(); } } ); String sEdit = UITexts.implementationsBlock_btnEdit; btnEdit = SWTUtil.createPushButton( buttonsComp, sEdit ); btnEdit.addListener( SWT.Selection, new Listener() { @Override public void handleEvent( final Event evt ) { editHsImplementation(); } } ); String sRemove = UITexts.implementationsBlock_btnRemove; btnRemove = SWTUtil.createPushButton( buttonsComp, sRemove ); btnRemove.addListener( SWT.Selection, new Listener() { @Override public void handleEvent( final Event evt ) { removeSelectedInstallations(); } } ); String sDetect = UITexts.cabalImplsBlock_btnAutoDetect; btnAutoDetect = SWTUtil.createPushButton( buttonsComp, sDetect ); btnAutoDetect.addListener( SWT.Selection, new Listener() { @Override public void handleEvent (final Event ev) { autoDetectGHCImpls(); } }); } private void createViewer() { viewer = new CheckboxTableViewer( table ); viewer.setLabelProvider( new HsImplementationsLP() ); viewer.setContentProvider( new HsImplementationsCP( installations ) ); // by default, sort by name sortByName(); viewer.addSelectionChangedListener( new ISelectionChangedListener() { @Override public void selectionChanged( final SelectionChangedEvent evt ) { enableButtons(); } } ); viewer.addCheckStateListener( new ICheckStateListener() { @Override public void checkStateChanged( final CheckStateChangedEvent event ) { if( event.getChecked() ) { IHsImplementation element = ( IHsImplementation )event.getElement(); setCheckedHsImplementation( element.getName() ); } else { setCheckedHsImplementation( null ); } } } ); viewer.addDoubleClickListener( new IDoubleClickListener() { @Override public void doubleClick( final DoubleClickEvent e ) { if( !viewer.getSelection().isEmpty() ) { editHsImplementation(); } } } ); } private void restoreColumnWidths( final IDialogSettings settings, final String qualifier ) { int columnCount = table.getColumnCount(); for( int i = 0; i < columnCount; i++ ) { int width = -1; try { width = settings.getInt( qualifier + KEY_COLUMN_WIDTH + i ); } catch( final NumberFormatException numfex ) { // ignored } if( width <= 0 ) { //table.getColumn( i ).pack(); table.getColumn( i ).setWidth( DEFAULT_WIDTH ); } else { table.getColumn( i ).setWidth( width ); } } } private void createSpacer( final Composite buttonsComp ) { Label separator = new Label( buttonsComp, SWT.NONE ); separator.setVisible( false ); GridData gd = new GridData( SWT.FILL, SWT.BEGINNING, true, false ); gd.heightHint = 4; separator.setLayoutData( gd ); } private void removeSelectedInstallations() { IStructuredSelection ssel = ( IStructuredSelection )viewer.getSelection(); IHsImplementation[] insts = new IHsImplementation[ ssel.size() ]; @SuppressWarnings("unchecked") Iterator<IHsImplementation> iter = ssel.iterator(); int i = 0; while( iter.hasNext() ) { insts[ i ] = iter.next(); i++; } removeHsImplementations( insts ); } private void removeHsImplementations( final IHsImplementation[] insts ) { IStructuredSelection prev = ( IStructuredSelection )getSelection(); for( int i = 0; i < insts.length; i++ ) { installations.remove( insts[ i ] ); } viewer.refresh(); autoSelectSingle( prev ); } private void addHsImplementation() { IStructuredSelection prev = ( IStructuredSelection )getSelection(); HsImplementationDialog dialog = new HsImplementationDialog( table.getShell(), this, null ); dialog.setTitle( UITexts.implementationsBlock_dlgAdd ); if( dialog.open() == Window.OK ) { add( dialog.getResult() ); autoSelectSingle( prev ); } } private void editHsImplementation() { IStructuredSelection ssel = ( IStructuredSelection )viewer.getSelection(); if( !ssel.isEmpty() ) { HsImplementation impl = ( HsImplementation )ssel.getFirstElement(); Shell shell = table.getShell(); HsImplementationDialog dlg = new HsImplementationDialog( shell, this, impl ); dlg.setTitle( UITexts.implementationsBlock_dlgEdit ); if( dlg.open() == Window.OK ) { installations.remove( impl ); add( dlg.getResult() ); autoSelectSingle( ssel ); } } } private void autoSelectSingle( final IStructuredSelection prev ) { IStructuredSelection curr = ( IStructuredSelection )getSelection(); if( !curr.equals( prev ) || curr.isEmpty() ) { if( curr.size() == 0 && installations.size() == 1 ) { // pick a default automatically setSelection( new StructuredSelection( installations.get( 0 ) ) ); } } else { fireSelectionChanged(); } } private void autoDetectGHCImpls() { IHsImplementation def= getCheckedHsImplementation(); List<IHsImplementation> detect=CompilerManager.autodetectGHCImpls(); installations.clear(); installations.addAll( detect); viewer.refresh(); if (def!=null){ setCheckedHsImplementation( def.getName()); } } private final class Comparator_Version extends ViewerComparator { // interface methods of ViewerComparator //////////////////////////////////////// @Override public int compare( final Viewer viewer, final Object e1, final Object e2 ) { int result = super.compare( viewer, e1, e2 ); if( ( e1 instanceof IHsImplementation ) && ( e2 instanceof IHsImplementation ) ) { IHsImplementation left = ( IHsImplementation )e1; IHsImplementation right = ( IHsImplementation )e2; result = left.getVersion().compareTo( right.getVersion() ); } return result; } @Override public boolean isSorterProperty( final Object element, final String property ) { return true; } } private final class Comparator_Name extends ViewerComparator { // interface methods of ViewerComparator //////////////////////////////////////// @Override public int compare( final Viewer viewer, final Object e1, final Object e2 ) { int result = super.compare( viewer, e1, e2 ); if( ( e1 instanceof IHsImplementation ) && ( e2 instanceof IHsImplementation ) ) { IHsImplementation left = ( IHsImplementation )e1; IHsImplementation right = ( IHsImplementation )e2; result = left.getName().compareToIgnoreCase( right.getName() ); } return result; } @Override public boolean isSorterProperty( final Object element, final String property ) { return true; } } private final class Comparator_TypeName extends ViewerComparator { // interface methods of ViewerComparator //////////////////////////////////////// @Override public int compare( final Viewer viewer, final Object e1, final Object e2 ) { int result = super.compare( viewer, e1, e2 ); if( e1 instanceof IHsImplementation && e2 instanceof IHsImplementation ) { IHsImplementation left = ( IHsImplementation )e1; IHsImplementation right = ( IHsImplementation )e2; String leftType = left.getType().name(); String rightType = right.getType().name(); if( leftType.compareToIgnoreCase( rightType ) != 0 ) { result = leftType.compareToIgnoreCase( rightType ); } else { result = left.getName().compareToIgnoreCase( right.getName() ); } } return result; } @Override public boolean isSorterProperty( final Object elem, final String prop ) { return true; } } /** The internal content provider class */ class HsImplementationsCP implements IStructuredContentProvider { /** Initialization */ HsImplementationsCP( final List<IHsImplementation> installations ) { // Currently unused. } // interface methods of IStructuredContentProvider ////////////////////////////////////////////////// @Override public Object[] getElements( final Object input ) { return installations.toArray(); } @Override public void inputChanged( final Viewer viewer, final Object oldInput, final Object newInput ) { // unused } @Override public void dispose() { // unused } } /** The internal label provider class */ class HsImplementationsLP extends LabelProvider implements ITableLabelProvider { @Override public String getColumnText( final Object elem, final int column ) { String result = null; if( elem instanceof IHsImplementation ) { IHsImplementation impl = ( IHsImplementation ) elem; switch( column ) { case 0: result = impl.getName(); break; case 1: result = impl.getType().toString(); break; case 2: result = impl.getVersion(); break; } } else { result = elem.toString(); } return result; } @Override public Image getColumnImage( final Object elem, final int column ) { return null; } } }