/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation 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: * IBM Corporation - initial API and implementation * Mark Leone - Modifications for PyDev *******************************************************************************/ package org.python.pydev.editor.hover; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.PixelConverter; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxCellEditor; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.ICellEditorValidator; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Point; 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.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.progress.UIJob; import org.python.pydev.core.log.Log; import org.python.pydev.editor.actions.PyAction; import org.python.pydev.plugin.PydevPlugin; import org.python.pydev.plugin.StatusInfo; import org.python.pydev.plugin.preferences.IPreferenceConfigurationBlock; import org.python.pydev.plugin.preferences.PydevPrefs; import org.python.pydev.ui.EmulatedNativeCheckBoxLabelProvider; import org.python.pydev.ui.ScrolledPageContent; import org.python.pydev.ui.TableLayoutComposite; import org.python.pydev.utils.Messages; import org.python.pydev.utils.PyEditorMessages; import org.python.pydev.utils.SWTUtil; import org.python.pydev.utils.StatusUtil; /** * Configures PyDev Editor hover preferences. */ public class PyEditorHoverConfigurationBlock implements IPreferenceConfigurationBlock { private static final String DELIMITER = PyEditorMessages.PyEditorHoverConfigurationBlock_delimiter; private static final int ENABLED_PROP = 0; private static final int PRIORITY_PROP = 1; private static final int MODIFIER_PROP = 2; private static final int PREEMPT_PROP = 3; private String priorityHelpStr = " (Lowest number is highest priority)"; private class PyEditorTextHoverDescriptorLabelProvider extends EmulatedNativeCheckBoxLabelProvider { public PyEditorTextHoverDescriptorLabelProvider(ColumnViewer viewer) { super(viewer); } @Override public void addListener(ILabelProviderListener listener) { } @Override public void dispose() { } @Override public boolean isLabelProperty(Object element, String property) { return false; } @Override public void removeListener(ILabelProviderListener listener) { } @Override public void update(ViewerCell cell) { switch (cell.getColumnIndex()) { case ENABLED_PROP: cell.setText(((PyEditorTextHoverDescriptor) cell.getElement()).getLabel()); break; case PRIORITY_PROP: cell.setText(String.valueOf(((PyEditorTextHoverDescriptor) cell.getElement()).getPriority())); break; case MODIFIER_PROP: TableItem item = (TableItem) fHoverTableViewer.testFindItem(cell.getElement()); int index = fHoverTable.indexOf(item); cell.setText(fHoverDescs[index].fModifierString); break; case PREEMPT_PROP: cell.setImage(getImage(cell.getElement())); break; default: break; } } @Override protected boolean isChecked(Object element) { return ((PyEditorTextHoverDescriptor) element).isPreempt(); } } private class PyEditorTextHoverDescriptorContentProvider implements IStructuredContentProvider { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { // Do nothing since the viewer listens to resource deltas } @Override public void dispose() { } @Override public Object[] getElements(Object element) { return (Object[]) element; } } private class HoverTableEditingSupport extends EditingSupport { private int column; CellEditor editor; private TableViewer viewer; public HoverTableEditingSupport(final TableViewer viewer, int column) { super(viewer); this.viewer = viewer; this.column = column; } @Override protected CellEditor getCellEditor(Object element) { if (this.column == PRIORITY_PROP) { this.editor = new TextCellEditor(this.viewer.getTable()); editor.setValidator(new ICellEditorValidator() { @Override public String isValid(Object value) { boolean valid = true; if (!"".equals(value)) { try { int val = Integer.parseInt((String) value); if (val <= 0) { valid = false; } } catch (NumberFormatException | ClassCastException e) { valid = false; } } editor.getControl() .setBackground(valid ? null : Display.getDefault().getSystemColor(SWT.COLOR_RED)); return (valid ? null : "positive integer required"); } }); ((Text) ((TextCellEditor) editor).getControl()).selectAll(); } else if (this.column == PREEMPT_PROP) { this.editor = new CheckboxCellEditor(viewer.getTable()); } return this.editor; } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object) */ @Override protected boolean canEdit(Object element) { return true; } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object) */ @Override protected Object getValue(Object element) { PyEditorTextHoverDescriptor descr = (PyEditorTextHoverDescriptor) element; if (descr != null) { switch (this.column) { case PRIORITY_PROP: fTableLabel.setText( PyEditorMessages.PyEditorHoverConfigurationBlock_hoverPreferences + priorityHelpStr); fTableLabel.setStyleRange(priorityHelpRange); fTableLabel.getParent().layout(); return String.valueOf(descr.getPriority()); case PREEMPT_PROP: return descr.isPreempt(); default: } } return ""; } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object, java.lang.Object) */ @Override protected void setValue(Object element, Object value) { switch (this.column) { case PRIORITY_PROP: ((PyEditorTextHoverDescriptor) element).setPriority(Integer.parseInt((String) value)); handleSetPriority(Integer.parseInt((String) value)); fTableLabel.setText(PyEditorMessages.PyEditorHoverConfigurationBlock_hoverPreferences); fTableLabel.setStyleRange(null); fTableLabel.getParent().layout(); break; case PREEMPT_PROP: ((PyEditorTextHoverDescriptor) element) .setIsPreempt(!((PyEditorTextHoverDescriptor) element).isPreempt()); handleSetPreempt((Boolean) value); break; default: } this.editor.dispose(); } } private Text fModifierEditor; private Table fHoverTable; private CheckboxTableViewer fHoverTableViewer; private TableColumn fNameColumn; TableColumn fModifierColumn; private TableColumn fPriorityColumn; TableColumn fPreemptColumn; private int[] fColWidths = new int[] { 35, 15, 35, 15 }; private Map<TableColumn, Integer> fColWidthsMap = new HashMap<TableColumn, Integer>(); private Text fDescription; private StyleRange priorityHelpRange = null; private PreferencePage fMainPreferencePage; public static StatusInfo fStatus; private class KeyModifierListener implements KeyListener { private Text editor; private KeyModifierListener(Text editor) { this.editor = editor; } private boolean isModifierCandidate; @Override public void keyPressed(KeyEvent e) { isModifierCandidate = e.keyCode > 0 && e.character == 0 && e.stateMask == 0; } @Override public void keyReleased(KeyEvent e) { if (isModifierCandidate && e.stateMask > 0 && e.stateMask == e.stateMask && e.character == 0) { String text = editor.getText(); Point selection = editor.getSelection(); int i = selection.x - 1; while (i > -1 && Character.isWhitespace(text.charAt(i))) { i--; } boolean needsPrefixDelimiter = i > -1 && !String.valueOf(text.charAt(i)).equals(DELIMITER); i = selection.y; while (i < text.length() && Character.isWhitespace(text.charAt(i))) { i++; } boolean needsPostfixDelimiter = i < text.length() && !String.valueOf(text.charAt(i)).equals(DELIMITER); String insertString; if (needsPrefixDelimiter && needsPostfixDelimiter) { insertString = Messages.format( PyEditorMessages.PyEditorHoverConfigurationBlock_insertDelimiterAndModifierAndDelimiter, new String[] { Action.findModifierString(e.stateMask) }); } else if (needsPrefixDelimiter) { insertString = Messages.format( PyEditorMessages.PyEditorHoverConfigurationBlock_insertDelimiterAndModifier, new String[] { Action.findModifierString(e.stateMask) }); } else if (needsPostfixDelimiter) { insertString = Messages.format( PyEditorMessages.PyEditorHoverConfigurationBlock_insertModifierAndDelimiter, new String[] { Action.findModifierString(e.stateMask) }); } else { insertString = Action.findModifierString(e.stateMask); } if (insertString != null) { editor.insert(insertString); } } } }; private Button fCombineHovers; private Text fCombiningHoverModifierEditor; private Button fDebugShowVars; private Button fUseFirstHover; private Label fCombiningHoverLabel; private Button fUseHoverDivider; private Composite fComp; private GridData fDisabledLabelGridData; private Composite fButtonComp; private PyEditorTextHoverDescriptor[] fHoverDescs; private PyEditorTextHoverDescriptor fCombiningHover; private Label fModifierFieldLabel; private StyledText fTableLabel; public PyEditorHoverConfigurationBlock(PreferencePage mainPreferencePage) { Assert.isNotNull(mainPreferencePage); fMainPreferencePage = mainPreferencePage; } /** * Creates page for hover preferences. * * @param parent the parent composite * @return the control for the preference page */ @Override public Control createControl(final Composite parent) { priorityHelpRange = new StyleRange( PyEditorMessages.PyEditorHoverConfigurationBlock_hoverPreferences.length(), priorityHelpStr.length(), parent.getDisplay().getSystemColor(SWT.COLOR_BLUE), null); fComp = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(fComp); GridLayoutFactory.fillDefaults().numColumns(3).applyTo(fComp); fButtonComp = new Composite(fComp, SWT.BORDER); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(fButtonComp); fUseFirstHover = new Button(fButtonComp, SWT.RADIO | SWT.BORDER); fUseFirstHover.setText("Use highest priority Hover"); GridDataFactory.fillDefaults().applyTo(fUseFirstHover); fCombineHovers = new Button(fButtonComp, SWT.RADIO | SWT.BORDER); fCombineHovers.setText("Combine Hovers"); GridDataFactory.fillDefaults().applyTo(fCombineHovers); fCombineHovers.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { fCombiningHoverModifierEditor.setEnabled(fCombineHovers.getSelection()); fCombiningHoverLabel.setEnabled(fCombineHovers.getSelection()); showColumn(fPreemptColumn, fCombineHovers.getSelection()); showColumn(fModifierColumn, !fCombineHovers.getSelection()); fModifierFieldLabel.setEnabled(!fCombineHovers.getSelection()); } }); fCombiningHoverLabel = new Label(fComp, SWT.PUSH); fCombiningHoverLabel.setText("Combined Hovers Key Modifier:"); fCombiningHoverModifierEditor = new Text(fComp, SWT.BORDER); fCombiningHoverModifierEditor.addKeyListener(new KeyModifierListener(fCombiningHoverModifierEditor)); fCombiningHoverModifierEditor.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { handleModifierModified(fCombiningHoverModifierEditor); } }); GridDataFactory.fillDefaults().grab(true, false).applyTo(fCombiningHoverModifierEditor); ScrolledPageContent scrolled = new ScrolledPageContent(parent, SWT.H_SCROLL | SWT.V_SCROLL); scrolled.setExpandHorizontal(true); scrolled.setExpandVertical(true); Composite hoverComposite = new Composite(scrolled, SWT.NONE); GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.marginWidth = 0; layout.marginHeight = 0; hoverComposite.setLayout(layout); addFiller(hoverComposite); fTableLabel = new StyledText(hoverComposite, SWT.NONE); fTableLabel.setEditable(false); fTableLabel.setEnabled(false); fTableLabel.setBackground(fTableLabel.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); fTableLabel.setText(PyEditorMessages.PyEditorHoverConfigurationBlock_hoverPreferences); GridData gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalAlignment = GridData.BEGINNING; gd.horizontalSpan = 2; fTableLabel.setLayoutData(gd); TableLayoutComposite layouter = new TableLayoutComposite(hoverComposite, SWT.NONE); addColumnLayoutData(layouter, fColWidths); // Hover table fHoverTable = new Table(layouter, SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION | SWT.CHECK); fHoverTable.setHeaderVisible(true); fHoverTable.setLinesVisible(true); gd = new GridData(GridData.FILL_HORIZONTAL); gd.heightHint = SWTUtil.getTableHeightHint(fHoverTable, 10); gd.horizontalSpan = 2; gd.widthHint = new PixelConverter(parent).convertWidthInCharsToPixels(100); layouter.setLayoutData(gd); fHoverTable.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { handleHoverListSelection(); } @Override public void widgetDefaultSelected(SelectionEvent e) { } }); TableLayout tableLayout = new TableLayout(); fHoverTable.setLayout(tableLayout); fHoverTableViewer = new CheckboxTableViewer(fHoverTable); TableViewerColumn nameColumnViewer = new TableViewerColumn(fHoverTableViewer, SWT.NONE); nameColumnViewer.setLabelProvider(new PyEditorTextHoverDescriptorLabelProvider(fHoverTableViewer)); fNameColumn = nameColumnViewer.getColumn(); fNameColumn.setText(PyEditorMessages.PyEditorHoverConfigurationBlock_nameColumnTitle); fNameColumn.setResizable(true); TableViewerColumn priorityViewerColumn = new TableViewerColumn(fHoverTableViewer, SWT.NONE); priorityViewerColumn.setEditingSupport(new HoverTableEditingSupport(fHoverTableViewer, PRIORITY_PROP)); priorityViewerColumn.setLabelProvider(new PyEditorTextHoverDescriptorLabelProvider(fHoverTableViewer)); fPriorityColumn = priorityViewerColumn.getColumn(); fPriorityColumn.setText(PyEditorMessages.PyEditorHoverConfigurationBlock_priorityColumnTitle); fPriorityColumn.setResizable(true); TableViewerColumn modifierColumnViewer = new TableViewerColumn(fHoverTableViewer, SWT.NONE); modifierColumnViewer.setLabelProvider(new PyEditorTextHoverDescriptorLabelProvider(fHoverTableViewer)); fModifierColumn = modifierColumnViewer.getColumn(); fModifierColumn.setText(PyEditorMessages.PyEditorHoverConfigurationBlock_modifierColumnTitle); fModifierColumn.setResizable(true); TableViewerColumn preemptViewerColumn = new TableViewerColumn(fHoverTableViewer, SWT.NONE); preemptViewerColumn.setEditingSupport(new HoverTableEditingSupport(fHoverTableViewer, PREEMPT_PROP)); preemptViewerColumn.setLabelProvider(new PyEditorTextHoverDescriptorLabelProvider(fHoverTableViewer)); fPreemptColumn = preemptViewerColumn.getColumn(); fPreemptColumn.setText(PyEditorMessages.PyEditorHoverConfigurationBlock_preemptColumnTitle); fPreemptColumn.setResizable(true); fHoverTableViewer.setUseHashlookup(true); fHoverTableViewer.setContentProvider(new PyEditorTextHoverDescriptorContentProvider()); fHoverTableViewer.addCheckStateListener(new ICheckStateListener() { /* * @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.Che((CheckboxTreeViewer) fHoverTableViewer)nt) */ @Override public void checkStateChanged(CheckStateChangedEvent event) { String id = ((PyEditorTextHoverDescriptor) event.getElement()).getId(); if (id == null) { return; } PyEditorTextHoverDescriptor[] descriptors = fHoverDescs; PyEditorTextHoverDescriptor hover = null; int i = 0, length = fHoverDescs.length; while (i < length) { if (id.equals(descriptors[i].getId())) { hover = fHoverDescs[i]; hover.setIsEnabled(event.getChecked()); fModifierEditor.setEnabled(event.getChecked()); fHoverTableViewer.setSelection(new StructuredSelection(descriptors[i])); } i++; } handleHoverListSelection(); updateStatus(hover); } }); fHoverTableViewer.setSorter(new ViewerSorter() { @Override public int compare(Viewer viewer, Object e1, Object e2) { return ((PyEditorTextHoverDescriptor) e1).getPriority() .compareTo(((PyEditorTextHoverDescriptor) e2).getPriority()); } }); // Text field for modifier string fModifierFieldLabel = new Label(hoverComposite, SWT.LEFT); fModifierFieldLabel.setText(PyEditorMessages.PyEditorHoverConfigurationBlock_keyModifier); fModifierEditor = new Text(hoverComposite, SWT.BORDER); gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); fModifierEditor.setLayoutData(gd); fModifierEditor.addKeyListener(new KeyModifierListener(fModifierEditor)); fModifierEditor.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { handleModifierModified(fModifierEditor); } }); // Description Label label = new Label(hoverComposite, SWT.LEFT); label.setText(PyEditorMessages.PyEditorHoverConfigurationBlock_description); gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); gd.horizontalSpan = 2; label.setLayoutData(gd); fDescription = new Text(hoverComposite, SWT.LEFT | SWT.WRAP | SWT.MULTI | SWT.READ_ONLY | SWT.BORDER); SWTUtil.fixReadonlyTextBackground(fDescription); gd = new GridData(GridData.FILL_BOTH); gd.horizontalSpan = 2; fDescription.setLayoutData(gd); fDebugShowVars = new Button(parent, SWT.CHECK); fDebugShowVars.setText("Show variables values while debugging"); fDebugShowVars.setSelection(PyHoverPreferencesPage.getShowValuesWhileDebuggingOnHover()); fUseHoverDivider = new Button(parent, SWT.CHECK); fUseHoverDivider.setText("Add divider between contributions when combining hovers"); fUseHoverDivider.setSelection(PyHoverPreferencesPage.getUseHoverDelimiters()); scrolled.setContent(hoverComposite); final Point size = hoverComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT); scrolled.setMinSize(size.x, size.y); Dialog.applyDialogFont(scrolled); return fComp; } private void addColumnLayoutData(TableLayoutComposite layouter, int[] widths) { layouter.addColumnData(new ColumnWeightData(widths[0], true)); layouter.addColumnData(new ColumnWeightData(widths[1], true)); layouter.addColumnData(new ColumnWeightData(widths[2], true)); layouter.addColumnData(new ColumnWeightData(widths[3], true)); } private PyEditorTextHoverDescriptor[] getContributedHovers() { return PydevPlugin.getDefault().getPyEditorTextHoverDescriptors(); } @Override public void initialize() { //need to do this asynchronously, or it has no effect new UIJob("Show/Hide Column") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { showColumn(fPreemptColumn, PyHoverPreferencesPage.getCombineHoverInfo()); showColumn(fModifierColumn, !PyHoverPreferencesPage.getCombineHoverInfo()); fModifierFieldLabel.setEnabled(!fCombineHovers.getSelection()); return Status.OK_STATUS; } }.schedule(); doInit(true); } private void doInit(boolean refreshHovers) { if (refreshHovers) { fHoverDescs = getContributedHovers(); fCombiningHover = PydevPlugin.getCombiningHoverDescriptor(); } fHoverTableViewer.setInput(fHoverDescs); fHoverTableViewer.refresh(); initializeFields(); } void initializeFields() { fModifierEditor.setEnabled(false); fCombineHovers.setSelection(PyHoverPreferencesPage.getCombineHoverInfo()); fUseFirstHover.setSelection(!PyHoverPreferencesPage.getCombineHoverInfo()); fCombiningHoverModifierEditor.setEnabled(PyHoverPreferencesPage.getCombineHoverInfo()); fCombiningHoverLabel.setEnabled(PyHoverPreferencesPage.getCombineHoverInfo()); for (int i = 0; i < fHoverDescs.length; i++) { fHoverTable.getItem(i).setChecked(fHoverDescs[i].isEnabled()); } fHoverTableViewer.refresh(); fCombiningHoverModifierEditor.setText(fCombiningHover.fModifierString); } @Override public void performOk() { Integer modifierMask = 0; Integer priority = 0; Boolean preempt = false; PyEditorTextHoverDescriptor[] hoverDescripters = (PyEditorTextHoverDescriptor[]) fHoverTableViewer.getInput(); sortHoverDescriptors(); if (hoverDescripters != fHoverDescs) { Log.log("Expecting hoverDescripters to be == fHoverDescs"); } //save preferences for configured hovers for (int i = 0; i < hoverDescripters.length; i++) { PyEditorTextHoverDescriptor hoverDesc = fHoverDescs[i]; String modifier = hoverDesc.fModifierString; if (modifier == null || modifier.length() == 0) { modifier = PyEditorTextHoverDescriptor.NO_MODIFIER; } modifierMask = hoverDesc.fStateMask; priority = hoverDesc.getPriority(); preempt = hoverDesc.isPreempt(); Boolean enable = hoverDesc.isEnabled(); String hoverDescId = hoverDesc.getId(); PydevPrefs.getPreferenceStore().setValue( PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER + hoverDescId, modifier); PydevPrefs.getPreferenceStore().setValue( PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER_MASK + hoverDescId, modifierMask); PydevPrefs.getPreferenceStore().setValue( PyHoverPreferencesPage.KEY_TEXT_HOVER_PRIORITY + hoverDescId, priority); PydevPrefs.getPreferenceStore().setValue( PyHoverPreferencesPage.KEY_TEXT_HOVER_PREEMPT + hoverDescId, preempt); PydevPrefs.getPreferenceStore().setValue( PyHoverPreferencesPage.KEY_TEXT_HOVER_ENABLE + hoverDescId, enable); } //save preferences for the combining hover PydevPrefs.getPreferenceStore().setValue( PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER + fCombiningHover.getId(), fCombiningHover.getModifierString()); PydevPrefs.getPreferenceStore().setValue( PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER_MASK + fCombiningHover.getId(), fCombiningHover.fStateMask); PydevPrefs.getPreferenceStore().setValue( PyHoverPreferencesPage.KEY_TEXT_HOVER_ENABLE + fCombiningHover.getId(), PyHoverPreferencesPage.getCombineHoverInfo()); //save general hover preferences PydevPlugin.getDefault().getPreferenceStore().setValue(PyHoverPreferencesPage.COMBINE_HOVER_INFO, fCombineHovers.getSelection()); PydevPlugin.getDefault().getPreferenceStore().setValue( PyHoverPreferencesPage.SHOW_DEBUG_VARIABLES_VALUES_ON_HOVER, fDebugShowVars.getSelection()); PydevPlugin.getDefault().getPreferenceStore().setValue(PyHoverPreferencesPage.USE_HOVER_DIVIDER, fUseHoverDivider.getSelection()); PydevPlugin.getDefault().resetPyEditorTextHoverDescriptors(); PydevCombiningHover.installTextHovers(); } /** * Sort the hover descriptors to match the order they appear in the viewer, * which has a viewer sorter that sorts by priority */ private void sortHoverDescriptors() { Arrays.sort(fHoverDescs, new Comparator<PyEditorTextHoverDescriptor>() { @Override public int compare(PyEditorTextHoverDescriptor o1, PyEditorTextHoverDescriptor o2) { return o1.getPriority().compareTo(o2.getPriority()); } }); } @Override public void performCancel() { PydevPlugin.getDefault().resetPyEditorTextHoverDescriptors(); } @Override public void performDefaults() { restoreFromPreferences(); doInit(false); initializeFields(); updateStatus(null); setcheckStates(); if (!fHoverTableViewer.getSelection().isEmpty()) { handleHoverListSelection(); } } private void setcheckStates() { for (PyEditorTextHoverDescriptor hover : fHoverDescs) { fHoverTableViewer.setChecked(hover, hover.isEnabled()); } } /** * Computes the state mask for the given modifier string. * * @param modifiers the string with the modifiers, separated by '+', '-', ';', ',' or '.' * @return the state mask or -1 if the input is invalid */ public int computeStateMask(String modifiers) { if (modifiers == null) { return -1; } if (modifiers.length() == 0) { return SWT.NONE; } int stateMask = 0; StringTokenizer modifierTokenizer = new StringTokenizer(modifiers, ",;.:+-* "); //$NON-NLS-1$ while (modifierTokenizer.hasMoreTokens()) { int modifier = PyAction.findLocalizedModifier(modifierTokenizer.nextToken()); if (modifier == 0 || (stateMask & modifier) == modifier) { return -1; } stateMask = stateMask | modifier; } return stateMask; } private void restoreFromPreferences() { //restore settings for contributed hovers for (int i = 0; i < fHoverDescs.length; i++) { String modifierString = PydevPrefs.getPreferenceStore() .getDefaultString(PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER + fHoverDescs[i].getId()); if (PyEditorTextHoverDescriptor.NO_MODIFIER.equals(modifierString)) { modifierString = ""; //$NON-NLS-1$ } fHoverDescs[i].fModifierString = modifierString; fHoverDescs[i].fStateMask = computeStateMask(modifierString); if (fHoverDescs[i].fStateMask == -1) { try { fHoverDescs[i].fStateMask = Integer .parseInt(PydevPrefs.getPreferenceStore().getString( PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER_MASK + fHoverDescs[i].getId())); } catch (NumberFormatException ex) { fHoverDescs[i].fStateMask = -1; } } fHoverDescs[i].setIsEnabled(PydevPlugin.getDefault().getPreferenceStore() .getDefaultBoolean(PyHoverPreferencesPage.KEY_TEXT_HOVER_ENABLE + fHoverDescs[i].getId())); fHoverDescs[i].setPriority(PydevPlugin.getDefault().getPreferenceStore() .getDefaultInt(PyHoverPreferencesPage.KEY_TEXT_HOVER_PRIORITY + fHoverDescs[i].getId())); fHoverDescs[i].setIsPreempt(PydevPlugin.getDefault().getPreferenceStore() .getDefaultBoolean(PyHoverPreferencesPage.KEY_TEXT_HOVER_PREEMPT + fHoverDescs[i].getId())); fHoverTableViewer.refresh(); sortHoverDescriptors(); } //restore settings for combining hover String modifierString = PydevPrefs.getPreferenceStore().getDefaultString( PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER + fCombiningHover.getId()); if (PyEditorTextHoverDescriptor.NO_MODIFIER.equals(modifierString)) { modifierString = ""; //$NON-NLS-1$ } fCombiningHover.fModifierString = modifierString; //state mask fCombiningHover.fStateMask = PyEditorTextHoverDescriptor.computeStateMask(modifierString); if (fCombiningHover.fStateMask == -1) { // Fallback: use stored modifier masks try { fCombiningHover.fStateMask = Integer.parseInt(PydevPrefs.getPreferenceStore().getString( PyHoverPreferencesPage.KEY_TEXT_HOVER_MODIFIER_MASK + fCombiningHover.getId())); } catch (NumberFormatException ex) { fCombiningHover.fStateMask = -1; } // Fix modifier string int stateMask = fCombiningHover.fStateMask; if (stateMask == -1) { fCombiningHover.fModifierString = ""; //$NON-NLS-1$ } else { fCombiningHover.fModifierString = PyAction.getModifierString(stateMask); } } } private void handleModifierModified(Text source) { int i = fHoverTable.getSelectionIndex(); PyEditorTextHoverDescriptor hover = null; Text editor = source; if (source == fCombiningHoverModifierEditor) { hover = PydevPlugin.getCombiningHoverDescriptor(); } else { if (i < 0) { return; } hover = fHoverDescs[i]; } String modifiers = editor.getText(); hover.fModifierString = modifiers; hover.fStateMask = PyEditorTextHoverDescriptor.computeStateMask(modifiers); // update table if (!fHoverTableViewer.isCellEditorActive() && i >= 0) { fHoverTableViewer.refresh(fHoverDescs[i]); } updateStatus(hover); } private void handleSetPriority(Integer priority) { int i = fHoverTable.getSelectionIndex(); if (i == -1) { return; } PyEditorTextHoverDescriptor hover = fHoverDescs[i]; hover.setPriority(priority); /** * Ensure hover configs and hover descriptors are in same order */ fHoverTableViewer.update(fHoverDescs[i], null); sortHoverDescriptors(); fHoverTableViewer.refresh(); updateStatus(hover); } public void handleSetPreempt(Boolean preempt) { int i = fHoverTable.getSelectionIndex(); if (i == -1) { return; } PyEditorTextHoverDescriptor hover = fHoverDescs[i]; hover.setIsPreempt(preempt); // update table fHoverTableViewer.refresh(((IStructuredSelection) fHoverTableViewer.getSelection()).getFirstElement()); updateStatus(hover); } private void handleHoverListSelection() { int i = fHoverTable.getSelectionIndex(); if (i == -1) { if (fHoverTable.getSelectionCount() == 0) { fModifierEditor.setEnabled(false); } return; } PyEditorTextHoverDescriptor hover = fHoverDescs[i]; boolean enabled = hover.isEnabled(); fModifierEditor.setEnabled(enabled && !fCombineHovers.getSelection()); fModifierEditor.setText(hover.fModifierString); String description = fHoverDescs[i].getDescription(); if (description == null) { description = ""; //$NON-NLS-1$ } fDescription.setText(description); } IStatus getStatus() { if (fStatus == null) { fStatus = new StatusInfo(); } return fStatus; } private void updateStatus(PyEditorTextHoverDescriptor hover) { if (hover != null && hover.isEnabled() && hover.fStateMask == -1) { fStatus = new StatusInfo(IStatus.ERROR, Messages.format(PyEditorMessages.PyEditorHoverConfigurationBlock_modifierIsNotValid, hover.fModifierString)); } else { fStatus = new StatusInfo(); } int i = 0; Map<Integer, String> stateMasks = new HashMap<Integer, String>(fHoverDescs.length); while (fStatus.isOK() && i < fHoverDescs.length) { if (fHoverDescs[i].isEnabled()) { String label = fHoverDescs[i].getLabel(); Integer stateMask = new Integer(fHoverDescs[i].fStateMask); if (fHoverDescs[i].fStateMask == -1) { fStatus = new StatusInfo(IStatus.ERROR, Messages.format( PyEditorMessages.PyEditorHoverConfigurationBlock_modifierIsNotValidForHover, new String[] { fHoverDescs[i].fModifierString, label })); /* The JDT implementation prohibits duplicate hovers with the same modifier. * We permit duplicates because PyDev hovers have a priority attribute, and the highest * priority hover with a given modifier will be selected at runtime. */ } else { stateMasks.put(stateMask, label); } } i++; } fMainPreferencePage.setValid(fStatus.isOK()); StatusUtil.applyToStatusLine(fMainPreferencePage, fStatus); } private void addFiller(Composite composite) { PixelConverter pixelConverter = new PixelConverter(composite); Label filler = new Label(composite, SWT.LEFT); GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL); gd.horizontalSpan = 2; gd.heightHint = pixelConverter.convertHeightInCharsToPixels(1) / 2; filler.setLayoutData(gd); } /* * @see DialogPage#dispose() */ @Override public void dispose() { // nothing to dispose } public void showColumn(TableColumn column, boolean show) { if (column.getWidth() != 0) { fColWidthsMap.put(column, column.getWidth()); } if (fColWidthsMap.get(column) != null) { column.setWidth(show ? fColWidthsMap.get(column) : 0); } } }