// 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;
}
}
}