/*=============================================================================#
# Copyright (c) 2011-2016 Stephan Wahlbrink (WalWare.de) and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# Contributors:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.docmlet.tex.internal.ui.config;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ICheckStateProvider;
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.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Label;
import de.walware.ecommons.IStatusChangeListener;
import de.walware.ecommons.databinding.jface.DataBindingSupport;
import de.walware.ecommons.preferences.core.Preference;
import de.walware.ecommons.preferences.ui.ConfigurationBlock;
import de.walware.ecommons.preferences.ui.ConfigurationBlockPreferencePage;
import de.walware.ecommons.preferences.ui.ManagedConfigurationBlock;
import de.walware.ecommons.ui.util.LayoutUtil;
import de.walware.ecommons.ui.util.ViewerUtil;
import de.walware.docmlet.tex.core.commands.LtxCommandCategories;
import de.walware.docmlet.tex.core.commands.LtxCommandCategories.Category;
import de.walware.docmlet.tex.core.commands.LtxCommandDefinitions;
import de.walware.docmlet.tex.core.commands.TexCommand;
import de.walware.docmlet.tex.core.commands.TexCommandSet;
import de.walware.docmlet.tex.ui.TexCommandLabelProvider;
public class LtxCommandsPreferencePage extends ConfigurationBlockPreferencePage {
public LtxCommandsPreferencePage() {
}
@Override
protected ConfigurationBlock createConfigurationBlock() {
return new TexCommandsConfigurationBlock(createStatusChangedListener());
}
}
class TexCommandsConfigurationBlock extends ManagedConfigurationBlock {
private static class CatContentProvider implements ITreeContentProvider {
private LtxCommandCategories fCategories;
@Override
public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
fCategories = (LtxCommandCategories) newInput;
}
@Override
public Object[] getElements(final Object inputElement) {
final List<Category> categories = fCategories.getCategories();
return categories.toArray(new Category[categories.size()]);
}
@Override
public boolean hasChildren(final Object element) {
return (element instanceof Category);
}
@Override
public Object[] getChildren(final Object parentElement) {
final List<TexCommand> commands = ((Category) parentElement).getCommands();
return commands.toArray(new TexCommand[commands.size()]);
}
@Override
public Object getParent(final Object element) {
if (element instanceof TexCommand) {
return fCategories.getCategory((TexCommand) element);
}
return null;
}
@Override
public void dispose() {
fCategories = null;
}
}
private static final int DETAIL_CHECK_ASSIST_TEXT = 0;
private static final int DETAIL_CHECK_ASSIST_MATH = 1;
private static final int DETAIL_CHECK_SIZE = 2;
private CheckboxTreeViewer fTreeViewer;
private final Button[] fDetailCheckControls = new Button[DETAIL_CHECK_SIZE];
private final Preference<Set<String>>[] fDetailCheckPrefs = new Preference[DETAIL_CHECK_SIZE];
private final Set<String> fEnabled= new HashSet<>();
private final Set<String>[] fDetailCheckEnabled = new HashSet[DETAIL_CHECK_SIZE];
public TexCommandsConfigurationBlock(final IStatusChangeListener statusListener) {
super(null, statusListener);
for (int i = 0; i < DETAIL_CHECK_SIZE; i++) {
fDetailCheckEnabled[i]= new HashSet<>();
}
}
@Override
public void createBlockArea(final Composite pageComposite) {
final Map<Preference<?>, String> prefs= new HashMap<>();
prefs.put(TexCommandSet.MASTER_COMMANDS_INCLUDE_PREF, null);
prefs.put(fDetailCheckPrefs[DETAIL_CHECK_ASSIST_TEXT]= TexCommandSet.TEXT_COMMANDS_INCLUDE_PREF, null);
prefs.put(fDetailCheckPrefs[DETAIL_CHECK_ASSIST_MATH]= TexCommandSet.MATH_COMMANDS_INCLUDE_PREF, null);
setupPreferenceManager(prefs);
final LtxCommandCategories categories = new LtxCommandCategories(LtxCommandDefinitions.getAllCommands());
final Composite composite = new Composite(pageComposite, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
composite.setLayout(LayoutUtil.applyCompositeDefaults(new GridLayout(), 2));
{ final Control tree = createTree(categories, composite);
tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
}
{ final Control detail = createDetailComposite(composite);
detail.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
}
// Binding
updateControls();
fTreeViewer.getTree().select(fTreeViewer.getTree().getItem(0));
}
private Control createTree(final LtxCommandCategories categories, final Composite composite) {
fTreeViewer = new CheckboxTreeViewer(composite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
fTreeViewer.setContentProvider(new CatContentProvider());
fTreeViewer.addCheckStateListener(new ICheckStateListener() {
@Override
public void checkStateChanged(final CheckStateChangedEvent event) {
final Object element = event.getElement();
Category category = null;
if (element instanceof Category) {
category = (Category) element;
final List<TexCommand> commands = category.getCommands();
if (event.getChecked()) {
for (final TexCommand command : commands) {
fEnabled.add(command.getControlWord());
}
}
else {
for (final TexCommand command : commands) {
fEnabled.remove(command.getControlWord());
}
}
}
else if (element instanceof TexCommand) {
final TexCommand command = (TexCommand) element;
if (event.getChecked()) {
fEnabled.add(command.getControlWord());
}
else {
fEnabled.remove(command.getControlWord());
}
category = categories.getCategory(command);
}
if (category != null) {
fTreeViewer.refresh(category, true);
}
}
});
fTreeViewer.setCheckStateProvider(new ICheckStateProvider() {
@Override
public boolean isChecked(final Object element) {
if (element instanceof Category) {
final List<TexCommand> commands = ((Category) element).getCommands();
for (final TexCommand command : commands) {
if (fEnabled.contains(command.getControlWord())) {
return true;
}
}
return false;
}
if (element instanceof TexCommand) {
return (fEnabled.contains(((TexCommand) element).getControlWord()));
}
return false;
}
@Override
public boolean isGrayed(final Object element) {
if (element instanceof Category) {
int check = 0x0;
final List<TexCommand> commands = ((Category) element).getCommands();
for (final TexCommand command : commands) {
check |= fEnabled.contains(command.getControlWord()) ? 0x1 : 0x2;
if (check == (0x1 | 0x2)) {
return true;
}
}
}
return false;
}
});
fTreeViewer.setLabelProvider(new TexCommandLabelProvider());
fTreeViewer.setInput(categories);
ViewerUtil.addDoubleClickExpansion(fTreeViewer);
return fTreeViewer.getControl();
}
protected Object[] getSelectedElements() {
return ((IStructuredSelection) fTreeViewer.getSelection()).toArray();
}
protected Control createDetailComposite(final Composite parent) {
final Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(LayoutUtil.applyCompositeDefaults(new GridLayout(), 1));
{ final Label label = new Label(composite, SWT.NONE);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
label.setText("Region:");
}
{ final Button button = new Button(composite, SWT.CHECK);
final GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
gd.horizontalIndent = LayoutUtil.defaultSmallIndent();
button.setLayoutData(gd);
button.setText("&Text");
fDetailCheckControls[DETAIL_CHECK_ASSIST_TEXT] = button;
registerDetailCheck(DETAIL_CHECK_ASSIST_TEXT);
}
{ final Button button = new Button(composite, SWT.CHECK);
final GridData gd = new GridData(SWT.FILL, SWT.CENTER, true, false);
gd.horizontalIndent = LayoutUtil.defaultSmallIndent();
button.setLayoutData(gd);
button.setText("&Math");
fDetailCheckControls[DETAIL_CHECK_ASSIST_MATH] = button;
registerDetailCheck(DETAIL_CHECK_ASSIST_MATH);
}
LayoutUtil.addSmallFiller(composite, true);
return composite;
}
private void registerDetailCheck(final int id) {
final Button button = fDetailCheckControls[id];
final Set<String> enabled = fDetailCheckEnabled[id];
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
button.setGrayed(false);
final Object[] elements = getSelectedElements();
if (button.getSelection()) {
for (final Object element : elements) {
if (element instanceof Category) {
final List<TexCommand> commands = ((Category) element).getCommands();
for (final TexCommand command : commands) {
enabled.add(command.getControlWord());
}
}
else if (element instanceof TexCommand) {
enabled.add(((TexCommand) element).getControlWord());
}
}
}
else {
for (final Object element : elements) {
if (element instanceof Category) {
final List<TexCommand> commands = ((Category) element).getCommands();
for (final TexCommand command : commands) {
enabled.remove(command.getControlWord());
}
}
else if (element instanceof TexCommand) {
enabled.remove(((TexCommand) element).getControlWord());
}
}
}
}
});
fTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
updateDetail();
}
});
}
private void updateDetail() {
final Object[] elements = getSelectedElements();
if (elements.length == 0) {
for (int i = 0; i < DETAIL_CHECK_SIZE; i++) {
fDetailCheckControls[i].setSelection(false);
fDetailCheckControls[i].setGrayed(false);
fDetailCheckControls[i].setEnabled(false);
}
}
final int[] state = new int[DETAIL_CHECK_SIZE];
for (final Object element : elements) {
if (element instanceof Category) {
final List<TexCommand> commands = ((Category) element).getCommands();
for (final TexCommand command : commands) {
for (int i = 0; i < DETAIL_CHECK_SIZE; i++) {
state[i] = fDetailCheckEnabled[i].contains(command.getControlWord()) ? 0x1 : 0x2;
}
}
}
else if (element instanceof TexCommand) {
final TexCommand command = (TexCommand) element;
for (int i = 0; i < DETAIL_CHECK_SIZE; i++) {
state[i] = fDetailCheckEnabled[i].contains(command.getControlWord()) ? 0x1 : 0x2;
}
}
}
for (int i = 0; i < DETAIL_CHECK_SIZE; i++) {
fDetailCheckControls[i].setEnabled(true);
if (state[i] == 0x3) {
fDetailCheckControls[i].setSelection(true);
fDetailCheckControls[i].setGrayed(true);
}
else {
fDetailCheckControls[i].setSelection(state[i] == 0x1);
fDetailCheckControls[i].setGrayed(false);
}
}
}
@Override
protected void addBindings(final DataBindingSupport db) {
}
@Override
protected void updateControls() {
super.updateControls();
fEnabled.clear();
fEnabled.addAll(getPreferenceValue(TexCommandSet.MASTER_COMMANDS_INCLUDE_PREF));
for (int i = 0; i < DETAIL_CHECK_SIZE; i++) {
fDetailCheckEnabled[i].clear();
fDetailCheckEnabled[i].addAll(getPreferenceValue(fDetailCheckPrefs[i]));
}
fTreeViewer.refresh();
updateDetail();
}
private void save() {
setPrefValue(TexCommandSet.MASTER_COMMANDS_INCLUDE_PREF, fEnabled);
for (int i = 0; i < DETAIL_CHECK_SIZE; i++) {
setPrefValue(fDetailCheckPrefs[i], fDetailCheckEnabled[i]);
}
}
@Override
public void performApply() {
save();
super.performApply();
}
@Override
public boolean performOk() {
save();
return super.performOk();
}
}