/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.spelling; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.FieldEditor; import org.eclipse.jface.preference.FieldEditorPreferencePage; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.resource.ResourceManager; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IOpenListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; 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.FileDialog; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Listener; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.events.IHyperlinkListener; import org.eclipse.ui.forms.widgets.Hyperlink; import org.xmind.ui.browser.BrowserSupport; import org.xmind.ui.browser.IBrowser; import org.xmind.ui.browser.IBrowserSupport; import org.xmind.ui.resources.ColorUtils; import org.xmind.ui.viewers.CheckListViewer; import com.swabunga.spell.engine.Configuration; public class SpellingCheckPrefPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { private static final String SPELLING_HELP_URL = "https://xmind.desk.com/customer/portal/articles/690243"; //$NON-NLS-1$ private static final Object DEFAULT_PLACEHOLDER = Messages.defaultDictionary; private static class Element { private String name; private boolean enabled; private ISpellCheckerDescriptor descriptor; private String path; public Element(String name, boolean enabled, ISpellCheckerDescriptor descriptor, String path) { this.name = name; this.enabled = enabled; this.descriptor = descriptor; this.path = path; } public String getName() { return name; } public boolean isEnabled() { return enabled; } public ISpellCheckerDescriptor getDescriptor() { return descriptor; } public void setDescriptor(ISpellCheckerDescriptor descriptor) { this.descriptor = descriptor; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } } private static class DictionaryLabelProvider extends LabelProvider { /* * (non-Javadoc) * @see * org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object) */ @Override public String getText(Object element) { if (element instanceof Element) return ((Element) element).getName(); return super.getText(element); } } private static class DictionaryComparator extends ViewerComparator { /** * */ public DictionaryComparator() { super(new Comparator<String>() { public int compare(String n1, String n2) { // Compare no-extension name: int s1 = n1.lastIndexOf('.'); int s2 = n2.lastIndexOf('.'); String p1 = s1 < 0 ? n1 : n1.substring(0, s1); String p2 = s2 < 0 ? n2 : n2.substring(0, s2); int c = p1.compareTo(p2); if (c != 0) return c; // Compare full name: return n1.compareTo(n2); } }); } /* * (non-Javadoc) * @see * org.eclipse.jface.viewers.ViewerComparator#category(java.lang.Object) */ @Override public int category(Object element) { if (element == DEFAULT_PLACEHOLDER) return 0; return 1; } } private class DictionarySelectionListener implements ISelectionChangedListener { /* * (non-Javadoc) * @see * org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged * (org.eclipse.jface.viewers.SelectionChangedEvent) */ public void selectionChanged(SelectionChangedEvent event) { updateDictionaryControls(); } } private class DictionaryOpenHandler implements IOpenListener { public void open(OpenEvent event) { selectSingleDictionary(event.getSelection()); } } private List<FieldEditor> settingFields = new ArrayList<FieldEditor>(); private Composite settingsParent; private ResourceManager resources; private CheckListViewer dictionaryViewer; private Button addButton; private Button removeButton; private List<Element> addElementReferences = new ArrayList<Element>(); private List<ISpellCheckerDescriptor> removeDescriptorReferences = new ArrayList<ISpellCheckerDescriptor>(); public SpellingCheckPrefPage() { super(Messages.SpellingPrefPage_title, FLAT); } public void init(IWorkbench workbench) { setPreferenceStore(SpellingPlugin.getDefault().getPreferenceStore()); } protected void createFieldEditors() { addField(new BooleanFieldEditor(SpellingPlugin.SPELLING_CHECK_ENABLED, Messages.enableSpellCheck, getFieldEditorParent())); addSpellingSettings(getFieldEditorParent()); addDictionariesPanel(getFieldEditorParent()); updateOptions(SpellingPlugin.isSpellingCheckEnabled()); updateDictionaryControls(); } private void addSpellingSettings(Composite composite) { resources = new LocalResourceManager(JFaceResources.getResources(), composite); settingsParent = createSettingsParent(composite); addSettingField(Configuration.SPELL_IGNOREUPPERCASE, Messages.ignoreAllCapital); addSettingField(Configuration.SPELL_IGNOREMIXEDCASE, Messages.ignoreMultiCapital); addSettingField(Configuration.SPELL_IGNOREINTERNETADDRESSES, Messages.ignoreWebAddress); addSettingField(Configuration.SPELL_IGNOREDIGITWORDS, Messages.ignoreNumberousAppendix); addSettingField(Configuration.SPELL_IGNORESENTENCECAPITALIZATION, Messages.ignoreFirstLowercaseSentences); } private void addSettingField(String name, String label) { FieldEditor field = new BooleanFieldEditor(name, label, settingsParent); addField(field); settingFields.add(field); } private Composite createSettingsParent(Composite parent) { GridLayout layout = new GridLayout(1, false); layout.marginTop = 7; layout.marginWidth = 0; layout.marginHeight = 0; layout.verticalSpacing = 0; layout.horizontalSpacing = 0; parent.setLayout(layout); Group group = new Group(parent, SWT.NONE); group.setText(Messages.options); group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout groupLayout = new GridLayout(1, false); groupLayout.marginWidth = 5; groupLayout.marginHeight = 5; groupLayout.verticalSpacing = 5; groupLayout.horizontalSpacing = 0; group.setLayout(groupLayout); return group; } private void updateOptions(boolean enabled) { settingsParent.setEnabled(enabled); for (FieldEditor field : settingFields) { field.setEnabled(enabled, settingsParent); } } private void addDictionariesPanel(Composite parent) { GridLayout layout = new GridLayout(1, false); layout.marginTop = 7; layout.marginWidth = 0; layout.marginHeight = 0; layout.verticalSpacing = 0; layout.horizontalSpacing = 0; parent.setLayout(layout); parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Group group = new Group(parent, SWT.NONE); group.setText(Messages.dictionaries); group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout groupLayout = new GridLayout(2, false); groupLayout.marginWidth = 5; groupLayout.marginHeight = 5; groupLayout.verticalSpacing = 5; groupLayout.horizontalSpacing = 5; group.setLayout(groupLayout); createDictionaryViewer(group); createDictionaryControls(group); createDetailsLink(group); } private void createDictionaryViewer(Composite parent) { dictionaryViewer = new CheckListViewer(parent, SWT.BORDER); dictionaryViewer.getControl().setBackground( parent.getDisplay().getSystemColor(SWT.COLOR_WHITE)); dictionaryViewer.getControl() .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); dictionaryViewer.setContentProvider(new ArrayContentProvider()); dictionaryViewer.setLabelProvider(new DictionaryLabelProvider()); dictionaryViewer.setComparator(new DictionaryComparator()); dictionaryViewer .addSelectionChangedListener(new DictionarySelectionListener()); dictionaryViewer.addOpenListener(new DictionaryOpenHandler()); dictionaryViewer.setInput(getInput()); initCheckStates(); } private Object getInput() { List<Object> objects = new ArrayList<Object>(); if (!getPreferenceStore().getBoolean( SpellingPlugin.DEFAULT_SPELLING_CHECKER_INVISIBLE)) { objects.add(DEFAULT_PLACEHOLDER); } List<ISpellCheckerDescriptor> descriptors = SpellCheckerRegistry .getInstance().getDescriptors(); for (ISpellCheckerDescriptor descriptor : descriptors) { String name = descriptor.getName(); boolean enabled = descriptor.isEnabled(); objects.add(new Element(name, enabled, descriptor, null)); } return objects; } private void initCheckStates() { if (dictionaryViewer == null || dictionaryViewer.getControl() == null || dictionaryViewer.getControl().isDisposed()) { return; } Object[] elements = ((IStructuredContentProvider) dictionaryViewer .getContentProvider()).getElements(dictionaryViewer.getInput()); for (Object element : elements) { boolean enabled = false; if (element instanceof Element) { enabled = ((Element) element).isEnabled(); } else if (element == DEFAULT_PLACEHOLDER) { enabled = !getPreferenceStore().getBoolean( SpellingPlugin.DEFAULT_SPELLING_CHECKER_DISABLED); } dictionaryViewer.setChecked(element, enabled); } } private void createDictionaryControls(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); GridLayout layout = new GridLayout(1, true); layout.marginHeight = 0; layout.marginWidth = 0; layout.verticalSpacing = 0; layout.horizontalSpacing = 0; composite.setLayout(layout); Composite buttonBar = new Composite(composite, SWT.NONE); buttonBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); GridLayout buttonBarLayout = new GridLayout(1, true); buttonBarLayout.marginWidth = 0; buttonBarLayout.marginHeight = 0; buttonBarLayout.verticalSpacing = 10; buttonBarLayout.horizontalSpacing = 0; buttonBar.setLayout(buttonBarLayout); createAddDictionaryButton(buttonBar); createRemoveDictionaryButton(buttonBar); //createDictionaryInfoPanel(composite); } private void createDetailsLink(Composite parent) { Hyperlink hyperlink = new Hyperlink(parent, SWT.SINGLE); hyperlink .setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); ((GridData) hyperlink.getLayoutData()).horizontalSpan = 2; hyperlink.setText(Messages.detailsLink_text); hyperlink.setForeground( (Color) resources.get(ColorUtils.toDescriptor("#006CF9"))); //$NON-NLS-1$ hyperlink.addHyperlinkListener(new IHyperlinkListener() { public void linkExited(HyperlinkEvent e) { } public void linkEntered(HyperlinkEvent e) { } public void linkActivated(HyperlinkEvent e) { final IBrowser browser; browser = BrowserSupport.getInstance() .createBrowser(IBrowserSupport.AS_EXTERNAL); SafeRunner.run(new SafeRunnable() { public void run() throws Exception { browser.openURL(SPELLING_HELP_URL); } }); } }); } private void createAddDictionaryButton(Composite parent) { addButton = new Button(parent, SWT.PUSH); addButton.setLayoutData( new GridData(SWT.FILL, SWT.CENTER, false, false)); addButton.setText(Messages.dictionaries_add); addButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { addDictionaryReference(); } }); } private void createRemoveDictionaryButton(Composite parent) { removeButton = new Button(parent, SWT.PUSH); removeButton.setLayoutData( new GridData(SWT.FILL, SWT.CENTER, false, false)); removeButton.setText(Messages.dictionaries_remove); removeButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { removeSelectedDictionaryReference(); } }); } @SuppressWarnings("unchecked") private void addDictionaryReference() { FileDialog dialog = new FileDialog(getShell(), SWT.OPEN); dialog.setFilterExtensions(new String[] { "*.dic;*.dict;*.txt;*.*" }); //$NON-NLS-1$ final String path = dialog.open(); if (path == null) return; List<String> nameExclusions = new ArrayList<String>(); if (dictionaryViewer.getInput() instanceof List<?>) { for (Object obj : (List<?>) dictionaryViewer.getInput()) { if (obj instanceof Element) { nameExclusions.add(((Element) obj).getName()); } } } String addedDictionaryName = SpellCheckerRegistry.getInstance() .getImportableDictFileName(new File(path), nameExclusions); Element addedElement = new Element(addedDictionaryName, false, null, path); if (dictionaryViewer.getInput() instanceof List<?>) { ((List<Object>) dictionaryViewer.getInput()).add(addedElement); } if (!addElementReferences.contains(addedElement)) { addElementReferences.add(addedElement); } dictionaryViewer.refresh(); } private void removeSelectedDictionaryReference() { Object selection = ((IStructuredSelection) dictionaryViewer .getSelection()).getFirstElement(); if (selection == null) return; // Confirm remove String name = ((ILabelProvider) dictionaryViewer.getLabelProvider()) .getText(selection); if (!MessageDialog.openConfirm(getShell(), Messages.dictionaries_remove_confirm_title, NLS.bind(Messages.dictionaries_remove_confirm_message, name))) return; removeDictionaryReference(selection); dictionaryViewer.refresh(); } @SuppressWarnings("unchecked") private void removeDictionaryReference(Object obj) { if (dictionaryViewer.getInput() instanceof List<?>) { ((List<Object>) dictionaryViewer.getInput()).remove(obj); } if (obj instanceof Element) { Element element = (Element) obj; if (element.getDescriptor() != null) { //remove old element removeDescriptorReferences.add(element.getDescriptor()); } else if (element.getPath() != null) { //remove new element addElementReferences.remove(element); } } } private void updateDictionaryControls() { ISelection selection = dictionaryViewer.getSelection(); if (selection instanceof IStructuredSelection) { boolean containDefault = ((IStructuredSelection) selection).toList() .contains(DEFAULT_PLACEHOLDER); removeButton.setEnabled(!dictionaryViewer.getSelection().isEmpty() && !containDefault); } else { removeButton.setEnabled(!dictionaryViewer.getSelection().isEmpty()); } } // /** // * @param composite // */ // private void createDictionaryInfoPanel(Composite composite) { // // } public void propertyChange(PropertyChangeEvent event) { FieldEditor field = (FieldEditor) event.getSource(); if (SpellingPlugin.SPELLING_CHECK_ENABLED .equals(field.getPreferenceName())) { updateOptions(((BooleanFieldEditor) field).getBooleanValue()); } super.propertyChange(event); } public void selectSingleDictionary(ISelection selection) { if (dictionaryViewer == null || dictionaryViewer.getControl() == null || dictionaryViewer.getControl().isDisposed()) { return; } if (selection instanceof IStructuredSelection) { Object selectedElement = ((IStructuredSelection) selection) .getFirstElement(); //disabled other element Object[] checkedElements = dictionaryViewer.getCheckedElements(); for (Object checkedElement : checkedElements) { if (selectedElement != checkedElement) { dictionaryViewer.setChecked(checkedElement, false); } } //enabled selected element dictionaryViewer.setChecked(selectedElement, true); } } @Override public boolean performOk() { List<Object> oldEnabledDictionaries = getEnabledDictionaries(); removeReferenceDictionarys(); addReferenceDictionarys(); //manage Default element getPreferenceStore().setValue( SpellingPlugin.DEFAULT_SPELLING_CHECKER_INVISIBLE, !((List<?>) dictionaryViewer.getInput()) .contains(DEFAULT_PLACEHOLDER)); for (int i = 0; i < dictionaryViewer.getItemCount(); i++) { Object element = dictionaryViewer.getElementAt(i); boolean enabled = dictionaryViewer.getChecked(element); if (element instanceof Element) { ((Element) element).getDescriptor().setEnabled(enabled); } else if (element == DEFAULT_PLACEHOLDER) { getPreferenceStore().setValue( SpellingPlugin.DEFAULT_SPELLING_CHECKER_DISABLED, !enabled); } } List<Object> newEnabledDictionaries = getEnabledDictionaries(); boolean needUpdated = !oldEnabledDictionaries .equals(newEnabledDictionaries); if (needUpdated) { SpellCheckerAgent.updateSpellChecker(); } return super.performOk(); } private List<Object> getEnabledDictionaries() { List<Object> objects = new ArrayList<Object>(); if (!getPreferenceStore() .getBoolean(SpellingPlugin.DEFAULT_SPELLING_CHECKER_INVISIBLE) && !getPreferenceStore().getBoolean( SpellingPlugin.DEFAULT_SPELLING_CHECKER_DISABLED)) { objects.add(DEFAULT_PLACEHOLDER); } List<ISpellCheckerDescriptor> descriptors = SpellCheckerRegistry .getInstance().getDescriptors(); for (ISpellCheckerDescriptor descriptor : descriptors) { if (descriptor.isEnabled()) { objects.add(descriptor); } } return objects; } @SuppressWarnings("unchecked") @Override protected void performDefaults() { if (!((List<?>) dictionaryViewer.getInput()) .contains(DEFAULT_PLACEHOLDER)) { ((List<Object>) dictionaryViewer.getInput()).add(0, DEFAULT_PLACEHOLDER); dictionaryViewer.refresh(); } for (int i = 0; i < dictionaryViewer.getItemCount(); i++) { Object element = dictionaryViewer.getElementAt(i); if (element instanceof Element) { removeDictionaryReference(element); } else if (element == DEFAULT_PLACEHOLDER) { dictionaryViewer.setChecked(element, true); } } super.performDefaults(); dictionaryViewer.refresh(); } private void addReferenceDictionarys() { for (Element element : addElementReferences) { addDictionary(element); } addElementReferences.clear(); } private void removeReferenceDictionarys() { for (ISpellCheckerDescriptor descriptor : removeDescriptorReferences) { removeDictionary(descriptor); } removeDescriptorReferences.clear(); } private void addDictionary(final Element element) { final String path = element.getPath(); if (path == null) return; try { ProgressMonitorDialog progress = new ProgressMonitorDialog( getShell()); progress.setOpenOnRun(false); progress.run(true, false, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask(Messages.addingDictionary, 1); SafeRunner.run(new SafeRunnable() { public void run() throws Exception { ISpellCheckerDescriptor descriptor = SpellCheckerRegistry .getInstance().importDictFile( new File(path), element.getName()); element.setPath(null); element.setDescriptor(descriptor); } }); monitor.done(); } }); } catch (InvocationTargetException e) { } catch (InterruptedException e) { } } private void removeDictionary(final ISpellCheckerDescriptor descriptor) { // Remove dictionary descriptor and local file try { ProgressMonitorDialog progress = new ProgressMonitorDialog( getShell()); progress.setOpenOnRun(false); progress.run(true, false, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask(Messages.removingDictionary, 1); SafeRunner.run(new SafeRunnable() { public void run() throws Exception { SpellCheckerRegistry.getInstance() .removeDictionary(descriptor); } }); monitor.done(); } }); } catch (InvocationTargetException e) { } catch (InterruptedException e) { } } @Override public void dispose() { if (addElementReferences != null) { addElementReferences.clear(); addElementReferences = null; } if (removeDescriptorReferences != null) { removeDescriptorReferences.clear(); removeDescriptorReferences = null; } super.dispose(); } }