package net.sf.eclipsefp.haskell.ui.internal.editors.cabal.forms.stanzas;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import net.sf.eclipsefp.haskell.core.cabalmodel.CabalSyntax;
import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescriptionStanza;
import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin;
import net.sf.eclipsefp.haskell.ui.internal.editors.cabal.forms.FormEntry;
import net.sf.eclipsefp.haskell.ui.internal.util.UITexts;
import net.sf.eclipsefp.haskell.util.FileUtil;
import net.sf.eclipsefp.haskell.util.PlatformUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.forms.widgets.FormToolkit;
/**
* Form entry for selecting modules from a project.
* @author Alejandro Serrano
*
*/
public class FormEntryModules extends FormEntry {
static final int EXPOSED_COL = 0;
static final int OTHER_COL = 1;
static final int NAME_COL = 2;
private final ArrayList<IOtherValueEntryListener> otherListeners = new ArrayList<>();
ArrayList<String> prevElements;
boolean onlyOne;
String exposedString;
FormToolkit toolkit;
Table table;
ArrayList<String> exposed;
ArrayList<String> other;
boolean ignoreModify = false;
HashMap<String, Button> exposedBoxes;
HashMap<String, Button> otherBoxes;
public FormEntryModules( final String exposedString ) {
this( exposedString, false );
}
public FormEntryModules( final String exposedString, final boolean onlyOne ) {
super();
this.exposedString = exposedString;
this.onlyOne = onlyOne;
this.exposed = new ArrayList<>();
this.other = new ArrayList<>();
this.exposedBoxes = new HashMap<>();
this.otherBoxes = new HashMap<>();
this.prevElements = new ArrayList<>();
}
@Override
public void init( final IProject project, final Composite parent,
final FormToolkit toolkit, final int style ) {
this.toolkit = toolkit;
table = toolkit.createTable( parent, SWT.SINGLE );
table.setHeaderVisible( true );
TableColumn exposedCol = new TableColumn( table, SWT.NULL );
exposedCol.setText( exposedString );
exposedCol.pack();
TableColumn otherCol = new TableColumn( table, SWT.NULL );
otherCol.setText( UITexts.cabalEditor_other_modules );
otherCol.pack();
TableColumn nameCol = new TableColumn( table, SWT.NULL );
nameCol.setText( UITexts.cabalEditor_module );
nameCol.pack();
}
public void changeExposedColumnName( final String newName ) {
TableColumn exposedCol = table.getColumn( EXPOSED_COL );
exposedCol.setText( newName );
}
@Override
public Control getControl() {
return table;
}
@Override
public int heightHint() {
return 170;
}
@SuppressWarnings ( "unchecked" )
public synchronized void setSourceFolders( final FormEntryModulesRoot root,
final boolean blockNotification ) {
synchronized( this ) {
if( root == null ) {
// If we have no data
table.clearAll();
for( Button b: exposedBoxes.values() ) {
b.dispose();
}
exposedBoxes.clear();
for( Button b: otherBoxes.values() ) {
b.dispose();
}
otherBoxes.clear();
prevElements.clear();
return;
}
Collection<String> sourceDirs = null;
if( root.getStanza().getProperties()
.containsKey( CabalSyntax.FIELD_HS_SOURCE_DIRS.getCabalName() ) ) {
//sourceDirs = root.getStanza().getProperties()
// .get( CabalSyntax.FIELD_HS_SOURCE_DIRS.getCabalName() );
sourceDirs=root.getStanza().getSourceDirs();
} else {
sourceDirs = Collections.singleton( "" );
}
ArrayList<String> modules = new ArrayList<>();
ModulesVisitor visitor = new ModulesVisitor( modules, sourceDirs );
try {
root.getProject().accept( visitor );
} catch( CoreException e ) {
HaskellUIPlugin.log( e );
}
PackageDescriptionStanza pkg = root.getDescription().getPackageStanza();
if( pkg != null ) {
if( pkg.getProperties().containsKey( CabalSyntax.FIELD_DATA_FILES.getCabalName() ) ) {
// There are data files, so Paths_package is also provided
modules.add( "Paths_"
+ pkg.getProperties().get( CabalSyntax.FIELD_NAME.getCabalName() ) );
}
}
// Show the items in alphabetical order
Collections.sort( modules );
// Check if it is the same
if( !getValueFrom( modules ).equals( getValueFrom( prevElements ) ) ) {
prevElements = modules;
// Set the new values for exposed and others
boolean changeToExposed = false;
boolean changeToOther = false;
for( String s: ( ArrayList<String> )exposed.clone() ) {
if( !modules.contains( s ) ) {
changeToExposed = true;
exposed.remove( s );
}
}
for( String s: ( ArrayList<String> )other.clone() ) {
if( !modules.contains( s ) ) {
changeToOther = true;
other.remove( s );
}
}
// Add the items to table
table.removeAll();
for( Button b: exposedBoxes.values() ) {
b.dispose();
}
exposedBoxes.clear();
for( Button b: otherBoxes.values() ) {
b.dispose();
}
otherBoxes.clear();
for( String mod: modules ) {
final TableItem item = new TableItem( table, SWT.NULL );
item.setText( new String[] { "", "", mod } );
// Add "exposed" check box
Button exposedButton = toolkit.createButton( table, "", SWT.CHECK );
exposedButton.setSelection( exposed.contains( mod ) );
exposedButton.pack();
exposedButton.addSelectionListener( new SelectionListener() {
@Override
public void widgetSelected( final SelectionEvent e ) {
if( !ignoreModify ) {
String mod = item.getText( NAME_COL );
if( exposed.contains( mod ) ) {
exposed.remove( mod );
} else {
if (onlyOne && exposed.size() > 0) {
// We have to deselect the other one
boolean previousIgnoreModify = ignoreModify;
ignoreModify = true;
for (String it : exposed) {
exposedBoxes.get( it ).setSelection( false );
}
ignoreModify = previousIgnoreModify;
exposed.clear();
exposed.add(mod);
} else {
exposed.add( mod );
Collections.sort( exposed );
}
}
FormEntryModules.this.notifyTextValueChanged();
}
}
@Override
public void widgetDefaultSelected( final SelectionEvent e ) {
// Do nothing
}
} );
exposedBoxes.put( mod, exposedButton );
TableEditor exposedEditor = new TableEditor( table );
exposedEditor.minimumWidth = exposedButton.getSize().x;
exposedEditor.horizontalAlignment = SWT.CENTER;
exposedEditor.setEditor( exposedButton, item, EXPOSED_COL );
// Add "other" check box
Button otherButton = toolkit.createButton( table, "", SWT.CHECK );
otherButton.setSelection( other.contains( mod ) );
otherButton.pack();
otherButton.addSelectionListener( new SelectionListener() {
@Override
public void widgetSelected( final SelectionEvent e ) {
if( !ignoreModify ) {
String mod = item.getText( NAME_COL );
if( other.contains( mod ) ) {
other.remove( mod );
} else {
other.add( mod );
Collections.sort( other );
}
FormEntryModules.this.notifyOtherValueChanged();
}
}
@Override
public void widgetDefaultSelected( final SelectionEvent e ) {
// Do nothing
}
} );
otherBoxes.put( mod, otherButton );
TableEditor otherEditor = new TableEditor( table );
otherEditor.minimumWidth = otherButton.getSize().x;
otherEditor.horizontalAlignment = SWT.CENTER;
otherEditor.setEditor( otherButton, item, OTHER_COL );
}
// ensure module names are visible
table.getColumn( 2 ).pack();
// Tell modifications
if( changeToExposed && !blockNotification ) {
notifyTextValueChanged();
}
if( changeToOther && !blockNotification ) {
notifyOtherValueChanged();
}
}
}
}
public ArrayList<String> getOrderedValue( final String value ) {
// Get ordered value
String newValue = value == null ? "" : value;
String[] elementsA = newValue.split( "," );
ArrayList<String> elements = new ArrayList<>();
for( String e: elementsA ) {
elements.add( e.trim() );
}
Collections.sort( elements );
return elements;
}
@Override
public synchronized void setValue( final String value,
final boolean blockNotification ) {
synchronized( this ) {
List<String> newValue = getOrderedValue( value );
if( getValueFrom( newValue ).equals( getValue() ) ) {
// We have the same value
return;
}
ignoreModify = true;
exposed.clear();
for( TableItem item: table.getItems() ) {
String mod = item.getText( NAME_COL );
if (exposedBoxes.containsKey( mod )) {
exposedBoxes.get( mod ).setSelection( newValue.contains( mod ) );
}
if( newValue.contains( mod ) ) {
exposed.add( mod );
}
}
ignoreModify = false;
if( !blockNotification ) {
this.notifyTextValueChanged();
}
}
}
public synchronized void setOtherModulesValue( final String value,
final boolean blockNotification ) {
synchronized( this ) {
List<String> newValue = getOrderedValue( value );
if( getValueFrom( newValue ).equals( getOtherModulesValue() ) ) {
// We have the same value
return;
}
ignoreModify = true;
other.clear();
for( TableItem item: table.getItems() ) {
String mod = item.getText( NAME_COL );
if (otherBoxes.containsKey( mod )) {
otherBoxes.get( mod ).setSelection( newValue.contains( mod ) );
}
if( newValue.contains( mod ) ) {
other.add( mod );
}
}
ignoreModify = false;
if( !blockNotification ) {
this.notifyOtherValueChanged();
}
}
}
String getValueFrom( final List<String> strings ) {
StringBuilder builder = new StringBuilder();
for( String s: strings ) {
if( builder.length() > 0 ) {
builder.append( ","+PlatformUtil.NL );
}
builder.append( s );
}
return builder.toString();
}
@Override
public String getValue() {
return getValueFrom( exposed );
}
public String getOtherModulesValue() {
return getValueFrom( other );
}
@Override
public void setEditable( final boolean editable ) {
if (!editable) {
setSourceFolders( null, true );
}
table.setEnabled( editable );
}
public void addOtherValueListener( final IOtherValueEntryListener listener ) {
this.otherListeners.add( listener );
}
public void removeOtherValueListener( final IOtherValueEntryListener listener ) {
this.otherListeners.remove( listener );
}
protected void notifyOtherValueChanged() {
for( IOtherValueEntryListener listener: otherListeners ) {
listener.otherTextValueChanged( this );
}
}
public static class ModulesVisitor implements IResourceVisitor {
public Collection<String> elts;
public Vector<String> possiblePrefixes;
public ModulesVisitor( final Collection<String> whereAdd,
final Collection<String> dirs ) {
this.elts = whereAdd;
this.possiblePrefixes = new Vector<>();
for( String dir: dirs ) {
String d=dir.trim();
// current or empty -> empty
this.possiblePrefixes.add(d.length()>0 && !d.equals(".")? d + "/" :"");
}
}
@Override
public boolean visit( final IResource resource ) {
if (resource.isDerived()){
return false;
}
if( resource instanceof IFile) {
IPath rel=resource.getProjectRelativePath();
String path = rel.toString();
for( String dir: possiblePrefixes ) {
if( path.startsWith( dir ) ) {
String filePath = path.substring( dir.length() );
if(FileUtil.hasHaskellExtension( resource )){
String module = filePath.substring( 0, filePath.length() - 3 )
.replace( '/', '.' );
this.elts.add( module );
} else if( FileUtil.hasLiterateExtension( resource )) {
String module = filePath.substring( 0, filePath.length() - 4 )
.replace( '/', '.' );
this.elts.add( module );
}
}
}
}
return true;
}
}
}