// Copyright (c) 2003-2005 by Leif Frenzel - see http://leiffrenzel.de package net.sf.eclipsefp.haskell.ui.internal.preferences; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import net.sf.eclipsefp.haskell.core.HaskellCorePlugin; import net.sf.eclipsefp.haskell.core.preferences.ICorePreferenceNames; import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin; import net.sf.eclipsefp.haskell.ui.internal.editors.cabal.CabalConfiguration; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.HaskellSourceViewerConfiguration; import net.sf.eclipsefp.haskell.ui.internal.preferences.editor.SyntaxPreviewer; import net.sf.eclipsefp.haskell.ui.internal.util.UITexts; import net.sf.eclipsefp.haskell.util.FileUtil; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.text.source.SourceViewerConfiguration; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Rectangle; 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.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Sash; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.model.WorkbenchViewerComparator; import org.eclipse.ui.preferences.ScopedPreferenceStore; import org.osgi.service.prefs.BackingStoreException; /** <p>the preference page for new Haskell projects. The user can pre-define * settings here that apply when the 'New Haskell Project' wizard creates * a new project.</p> * * @author Leif Frenzel */ public class NewHaskellProjectPP extends PreferencePage implements IWorkbenchPreferencePage { private final ArrayList<Button> alRadioButtons; private final ArrayList<Text> alTextControls; private final SelectionListener selectionListener; private final ModifyListener modifyListener; private Text txtSrcFolderName; //private Text txtDocFolderName; //private Text txtOutFolderName; //private Text txtTargetBinaryName; private Button btnProjectAsSourceFolder; private Button btnFoldersAsSourceFolder; private final Map<String,TemplateDef[]> templateDefs=new HashMap<>(); public NewHaskellProjectPP() { super( UITexts.preferences_project_title ); setDescription( UITexts.preferences_project_description ); alRadioButtons = new ArrayList<>(); alTextControls = new ArrayList<>(); selectionListener = new SelectionListener() { @Override public void widgetDefaultSelected( final SelectionEvent e ) { // unused } @Override public void widgetSelected( final SelectionEvent e ) { controlChanged( e.widget ); } }; modifyListener = new ModifyListener() { @Override public void modifyText( final ModifyEvent evt ) { controlModified( evt.widget ); } }; } public static void initializeDefaults( final IPreferenceStore store ) { store.setDefault( ICorePreferenceNames.FOLDERS_IN_NEW_PROJECT, true ); store.setDefault( ICorePreferenceNames.FOLDERS_SRC, FileUtil.DEFAULT_FOLDER_SRC ); //store.setDefault( ICorePreferenceNames.FOLDERS_DOC, "doc" ); // store.setDefault( ICorePreferenceNames.FOLDERS_OUT, "out" ); // store.setDefault( ICorePreferenceNames.TARGET_BINARY, "bin/theResult" ); } @Override public void init( final IWorkbench workbench ) { // empty implementation } // helping methods ////////////////// private Button addRadioButton( final Composite parent, final String label, final String key, final String value, final int indent ) { GridData gd = new GridData( GridData.HORIZONTAL_ALIGN_FILL ); gd.horizontalSpan = 2; gd.horizontalIndent = indent; Button result = new Button( parent, SWT.RADIO ); result.setText( label ); result.setData( new String[] { key, value } ); result.setLayoutData( gd ); boolean selected = value.equals( getPreferenceStore().getString( key ) ); result.setSelection( selected ); alRadioButtons.add( result ); return result; } private Text addTextControl( final Composite parent, final String labelText, final String key, final int indent ) { Label label = new Label( parent, SWT.NONE ); label.setText( labelText ); GridData gd = new GridData(); gd.horizontalIndent = indent; label.setLayoutData( gd ); gd = new GridData( GridData.FILL_HORIZONTAL ); gd.widthHint = convertWidthInCharsToPixels( 40 ); Text result = new Text( parent, SWT.SINGLE | SWT.BORDER ); result.setText( getPreferenceStore().getString( key ) ); result.setData( key ); result.setLayoutData( gd ); alTextControls.add( result ); result.addModifyListener( modifyListener ); return result; } @Override protected Control createContents( final Composite parent ) { initializeDialogUnits( parent ); Composite result = new Composite( parent, SWT.NONE ); GridLayout layout = new GridLayout(); layout.marginHeight = convertVerticalDLUsToPixels( IDialogConstants.VERTICAL_MARGIN ); layout.marginWidth = 0; layout.verticalSpacing = convertVerticalDLUsToPixels( 10 ); layout.horizontalSpacing = convertHorizontalDLUsToPixels( IDialogConstants.HORIZONTAL_SPACING ); layout.numColumns = 2; result.setLayout( layout ); createFoldersGroup( result ); validate(); createTemplatesGroup(result); Dialog.applyDialogFont( result ); return result; } private void createTemplatesGroup( final Composite result ) { final Group templateGroup = new Group( result, SWT.NONE ); GridLayout templateLayout = new GridLayout(); templateLayout.numColumns = 1; templateGroup.setLayout( templateLayout ); templateGroup.setText( UITexts.preferences_project_file_templates ); GridData gd = new GridData( GridData.FILL_BOTH ); gd.horizontalSpan = 2; templateGroup.setLayoutData( gd ); final TreeViewer tv=new TreeViewer( templateGroup,SWT.SINGLE ); final GridData dataTv= new GridData( GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH ) ; tv.getTree().setLayoutData(dataTv); tv.setComparator( new WorkbenchViewerComparator() ); tv.setContentProvider( new TemplateDefCP() ); SourceViewerConfiguration cabalConf=new CabalConfiguration(null); SourceViewerConfiguration haskellConf=new HaskellSourceViewerConfiguration( null ); TemplateDef[] cabals=new TemplateDef[9]; cabals[0]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL, UITexts.preferences_project_file_TEMPLATE_CABAL,cabalConf ); cabals[1]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL_EXE, UITexts.preferences_project_file_TEMPLATE_CABAL_EXE,cabalConf ); cabals[2]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL_LIBRARY, UITexts.preferences_project_file_TEMPLATE_CABAL_LIBRARY,cabalConf ); cabals[3]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL_SETUP, UITexts.preferences_project_file_TEMPLATE_CABAL_SETUP ,haskellConf); cabals[4]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL_HTF, UITexts.preferences_project_file_TEMPLATE_CABAL_HTF ,cabalConf); cabals[5]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL_TF, UITexts.preferences_project_file_TEMPLATE_CABAL_TF ,cabalConf); cabals[6]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL_STDIO, UITexts.preferences_project_file_TEMPLATE_CABAL_STDIO ,cabalConf); cabals[7]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL_DETAILED, UITexts.preferences_project_file_TEMPLATE_CABAL_DETAILED ,cabalConf); cabals[8]=new TemplateDef( ICorePreferenceNames.TEMPLATE_CABAL_TASTY, UITexts.preferences_project_file_TEMPLATE_CABAL_TASTY ,cabalConf); TemplateDef[] haskells=new TemplateDef[8]; haskells[0]=new TemplateDef( ICorePreferenceNames.TEMPLATE_GTK, UITexts.preferences_project_file_TEMPLATE_GTK,haskellConf); haskells[1]=new TemplateDef( ICorePreferenceNames.TEMPLATE_MAIN, UITexts.preferences_project_file_TEMPLATE_MAIN ,haskellConf); haskells[2]=new TemplateDef( ICorePreferenceNames.TEMPLATE_MODULE, UITexts.preferences_project_file_TEMPLATE_MODULE ,haskellConf); haskells[3]=new TemplateDef( ICorePreferenceNames.TEMPLATE_MODULE_HTF, UITexts.preferences_project_file_TEMPLATE_MODULE_HTF ,haskellConf); haskells[4]=new TemplateDef( ICorePreferenceNames.TEMPLATE_MAIN_HTF, UITexts.preferences_project_file_TEMPLATE_MAIN_HTF ,haskellConf); haskells[5]=new TemplateDef( ICorePreferenceNames.TEMPLATE_IMPORT_HTF, UITexts.preferences_project_file_TEMPLATE_IMPORT_HTF ,haskellConf); haskells[6]=new TemplateDef( ICorePreferenceNames.TEMPLATE_MODULE_TASTY, UITexts.preferences_project_file_TEMPLATE_MODULE_TASTY ,haskellConf); haskells[7]=new TemplateDef( ICorePreferenceNames.TEMPLATE_MAIN_TASTY, UITexts.preferences_project_file_TEMPLATE_MAIN_TASTY ,haskellConf); templateDefs.put(UITexts.preferences_project_file_templates_cabal,cabals); templateDefs.put(UITexts.preferences_project_file_templates_haskell,haskells); tv.setInput( templateDefs ); final Sash sash = new Sash(templateGroup, SWT.HORIZONTAL); final GridData dataS= new GridData( GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL ); //dataS.heightHint=6; sash.setLayoutData( dataS); final GridData dataSv= new GridData( GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH ); sash.addListener( SWT.Selection, new Listener() { @Override public void handleEvent( final Event event ) { Rectangle clientArea = templateGroup.getClientArea(); int newHeight = event.y; //clientArea.height - event.y - sash.getSize().y; if (newHeight != dataTv.heightHint) { dataTv.heightHint = newHeight- sash.getBounds().height; dataSv.heightHint = clientArea.height - event.y + 20; templateGroup.layout(); } } }); SyntaxPreviewer sv=new SyntaxPreviewer( templateGroup, HaskellUIPlugin.getDefault() .getPreferenceStore(), new HaskellSourceViewerConfiguration( null ),"" ); sv.getTextWidget().setLayoutData(dataSv ); //final StyledText st=new StyledText( templateGroup, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER ); //st.setLayoutData( new GridData( GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH ) ); sv.getDocument().set( "" ); sv.getTextWidget().setEditable( false ); final List<SyntaxPreviewer> svs=new ArrayList<>(); svs.add(sv); tv.addSelectionChangedListener( new ISelectionChangedListener() { @Override public void selectionChanged( final SelectionChangedEvent arg0 ) { Object o=((IStructuredSelection)arg0.getSelection()).getFirstElement(); if (svs.isEmpty()){ return; } SyntaxPreviewer sv=svs.get( 0 ); if (o instanceof TemplateDef){ TemplateDef td=(TemplateDef) o; sv.getTextWidget().dispose(); svs.clear(); final SyntaxPreviewer sv2=new SyntaxPreviewer( templateGroup, HaskellUIPlugin.getDefault() .getPreferenceStore(), td.getConfiguration() , td.getContent() ); sv2.getTextWidget().setLayoutData(dataSv); sv2.setEditable( true ); sv2.getTextWidget().addModifyListener( new ModifyListener() { @Override public void modifyText( final ModifyEvent arg0 ) { Object o=((IStructuredSelection)tv.getSelection()).getFirstElement(); if (o instanceof TemplateDef){ TemplateDef td=(TemplateDef) o; td.setContent( sv2.getDocument().get() ); } } } ); svs.add(sv2); templateGroup.layout( true ); } else { sv.getDocument().set( "" ); sv.setEditable( false ); } } } ); } private void createFoldersGroup( final Composite result ) { Group folderGroup = new Group( result, SWT.NONE ); GridLayout layout = new GridLayout(); layout.numColumns = 2; folderGroup.setLayout( layout ); GridData gd = new GridData( GridData.FILL_HORIZONTAL ); gd.horizontalSpan = 2; folderGroup.setLayoutData( gd ); folderGroup.setText( UITexts.preferences_project_folders ); createRadios( folderGroup ); createTexts( folderGroup ); } private void createRadios( final Group folderGroup ) { int indent = 0; String prefFolders = ICorePreferenceNames.FOLDERS_IN_NEW_PROJECT; btnProjectAsSourceFolder = addRadioButton( folderGroup, UITexts.preferences_project_use_project, prefFolders, IPreferenceStore.FALSE, indent ); btnProjectAsSourceFolder.addSelectionListener( selectionListener ); btnFoldersAsSourceFolder = addRadioButton( folderGroup, UITexts.preferences_project_use_folders, prefFolders, IPreferenceStore.TRUE, indent ); btnFoldersAsSourceFolder.addSelectionListener( selectionListener ); } private void createTexts( final Group folderGroup ) { int indent = convertWidthInCharsToPixels( 4 ); txtSrcFolderName = addTextControl( folderGroup, UITexts.preferences_project_source, ICorePreferenceNames.FOLDERS_SRC, indent ); // txtDocFolderName = addTextControl ( folderGroup, // UITexts.preferences_project_documentation, // ICorePreferenceNames.FOLDERS_DOC, // indent ); /* txtOutFolderName = addTextControl( folderGroup, "Output folder name:", ICorePreferenceNames.FOLDERS_OUT, indent ); txtTargetBinaryName = addTextControl( folderGroup, "Target binary name:", ICorePreferenceNames.TARGET_BINARY, indent );*/ Label l=new Label(folderGroup,SWT.NONE); l.setText( UITexts.preferences_project_output_note ); GridData gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan=2; l.setLayoutData( gd ); } private void validate() { boolean useFolders = btnFoldersAsSourceFolder.getSelection(); txtSrcFolderName.setEnabled( useFolders ); // txtDocFolderName.setEnabled( useFolders ); //txtOutFolderName.setEnabled( useFolders ); //txtTargetBinaryName.setEnabled( useFolders ); boolean valid = true; if( useFolders ) { valid = validateFolders(); } if( valid ) { setValid( true ); // setMessage( null ); setErrorMessage( null ); } } private boolean validateFolders() { boolean result; if ( txtSrcFolderName.getText().length() == 0 // || txtDocFolderName.getText().length() == 0 //&& txtOutFolderName.getText().length() == 0 ) { updateNonOkStatus( UITexts.preferences_project_folders_empty ); result = false; } else { IProject dmy = getWorkspace().getRoot().getProject( UITexts.preferences_project_invalid_project ); result = isValidPath( txtSrcFolderName.getText(), UITexts.preferences_project_invalid_source, dmy ); // && isValidPath (txtDocFolderName.getText(), UITexts.preferences_project_invalid_documentation, dmy); } return result; } private boolean isValidPath( final String folderName, final String name, final IProject dmy ) { boolean result = true; IPath path = dmy.getFullPath().append( folderName ); if( folderName.length() != 0 ) { IStatus status = getWorkspace().validatePath( path.toString(), IResource.FOLDER ); if( !status.isOK() ) { String message = NLS.bind( UITexts.preferences_project_invalid, name ) + status.getMessage(); updateNonOkStatus( message ); result = false; } } return result; } private IWorkspace getWorkspace() { return ResourcesPlugin.getWorkspace(); } private void updateNonOkStatus( final String message ) { setValid( false ); //setMessage( message, NONE ); setErrorMessage( message ); } private void controlChanged( final Widget widget ) { if( widget == btnFoldersAsSourceFolder || widget == btnProjectAsSourceFolder ) { validate(); } } private void controlModified( final Widget widget ) { if( widget == txtSrcFolderName // || widget == txtDocFolderName //|| widget == txtOutFolderName ) { validate(); } } /* @see PreferencePage#performDefaults() */ @Override protected void performDefaults() { IPreferenceStore store = getPreferenceStore(); for( int i = 0; i < alRadioButtons.size(); i++ ) { Button button = alRadioButtons.get( i ); String[] info = ( String[] )button.getData(); boolean sel = info[ 1 ].equals( store.getDefaultString( info[ 0 ] ) ); button.setSelection( sel ); } for( int i = 0; i < alTextControls.size(); i++ ) { Text text = alTextControls.get( i ); String key = ( String )text.getData(); text.setText( store.getDefaultString( key ) ); } for (String s:templateDefs.keySet()){ for (TemplateDef td:templateDefs.get( s )){ td.setContent( getPreferenceStore().getDefaultString( td.getPreference() ) ); } } validate(); super.performDefaults(); } /* @see IPreferencePage#performOk() */ @Override public boolean performOk() { IPreferenceStore store = getPreferenceStore(); for( int i = 0; i < alRadioButtons.size(); i++ ) { Button button = alRadioButtons.get( i ); if( button.getSelection() ) { String[] info = ( String[] )button.getData(); store.setValue( info[ 0 ], info[ 1 ] ); } } for( int i = 0; i < alTextControls.size(); i++ ) { Text text = alTextControls.get( i ); String key = ( String )text.getData(); store.setValue( key, text.getText() ); } for (String s:templateDefs.keySet()){ for (TemplateDef td:templateDefs.get( s )){ store.setValue( td.getPreference(), td.getContent() ); } } try { InstanceScope.INSTANCE.getNode(HaskellCorePlugin.getPluginId()).flush(); } catch( BackingStoreException ex ) { HaskellCorePlugin.log( ex ); } return super.performOk(); } @Override protected IPreferenceStore doGetPreferenceStore() { return new ScopedPreferenceStore(InstanceScope.INSTANCE, HaskellCorePlugin.getPluginId()); } private class TemplateDef{ private final String preference; private final String displayName; private String content; private final SourceViewerConfiguration configuration; public TemplateDef( final String preference, final String displayName, final SourceViewerConfiguration config ) { super(); this.preference = preference; this.displayName = displayName; this.content = getPreferenceStore().getString( preference ); this.configuration=config; } /** * @return the configuration */ public SourceViewerConfiguration getConfiguration() { return configuration; } public String getPreference() { return preference; } public String getDisplayName() { return displayName; } public String getContent() { return content; } public void setContent( final String content ) { this.content = content; } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return getDisplayName(); } } private class TemplateDefCP implements ITreeContentProvider { /* (non-Javadoc) * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ @Override public void dispose() { // NOOP } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) */ @Override public Object[] getChildren( final Object arg0 ) { if (arg0 instanceof Map<?, ?>){ Set<String> ss=templateDefs.keySet(); return ss.toArray(); } else if (arg0 instanceof String){ return templateDefs.get( arg0 ); } return new Object[0]; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getElements(java.lang.Object) */ @Override public Object[] getElements( final Object arg0 ) { return getChildren(arg0); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) */ @Override public Object getParent( final Object arg0 ) { return null; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) */ @Override public boolean hasChildren( final Object arg0 ) { return arg0 instanceof String; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) */ @Override public void inputChanged( final Viewer arg0, final Object arg1, final Object arg2 ) { // NOOP } } }