/** * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package org.python.pydev.debug.ui; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; 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.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.dialogs.SelectionDialog; import org.eclipse.ui.internal.WorkbenchMessages; import org.python.pydev.debug.model.PyExceptionBreakPointManager; import org.python.pydev.debug.ui.actions.PyExceptionListProvider; import com.aptana.shared_core.string.StringMatcher; public class PyConfigureExceptionDialog extends SelectionDialog { protected DefaultFilterMatcher fFilterMatcher = new DefaultFilterMatcher(); protected boolean updateInThread = true; // the visual selection widget group private Text filterPatternField; private Text addNewExceptionField; // providers for populating this dialog private ILabelProvider labelProvider; private IStructuredContentProvider contentProvider; private String filterPattern; // the root element to populate the viewer with private Object inputElement; private FilterJob filterJob; // the visual selection widget group CheckboxTableViewer listViewer; // sizing constants private final static int SIZING_SELECTION_WIDGET_HEIGHT = 250; private final static int SIZING_SELECTION_WIDGET_WIDTH = 300; // enable/disable breaking on the caught private Button uncaughtExceptionCheck; private Button caughtExceptionCheck; private boolean handleCaughtExceptions; private boolean handleUncaughtExceptions; protected static String SELECT_ALL_TITLE = WorkbenchMessages.SelectionDialog_selectLabel; protected static String DESELECT_ALL_TITLE = WorkbenchMessages.SelectionDialog_deselectLabel; public PyConfigureExceptionDialog(Shell parentShell, Object input, IStructuredContentProvider contentProvider, ILabelProvider labelProvider, String message) { super(parentShell); setTitle(WorkbenchMessages.ListSelection_title); this.inputElement = input; this.contentProvider = contentProvider; this.labelProvider = labelProvider; if (message != null) { setMessage(message); } else { setMessage(WorkbenchMessages.ListSelection_message); } } /** * * @param composite * the parent composite * @return the message label */ protected Label createMessageArea(Composite composite) { Label filterLabel = new Label(composite, SWT.NONE); filterLabel.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER, false, false, 2, 1)); filterLabel.setText("Enter a filter (* = any number of " + "characters, ? = any single character)" + "\nor an empty string for no filtering:"); filterPatternField = new Text(composite, SWT.BORDER); filterPatternField.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false)); return filterLabel; } /** * Add the selection and deselection buttons to the dialog. * * @param composite * org.eclipse.swt.widgets.Composite */ protected void createSelectionButtons(Composite composite) { Composite buttonComposite = new Composite(composite, SWT.NONE); GridLayout layout = new GridLayout(); layout.numColumns = 0; layout.marginWidth = 0; layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); buttonComposite.setLayout(layout); buttonComposite.setLayoutData(new GridData(SWT.END, SWT.TOP, true, false)); createSelectAll(buttonComposite); createDeselectAll(buttonComposite); } /** * Creates a Select All button and its respective listener. * * @param buttonComposite */ private void createSelectAll(Composite buttonComposite) { Button selectButton = createButton(buttonComposite, IDialogConstants.SELECT_ALL_ID, SELECT_ALL_TITLE, false); SelectionListener listener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { listViewer.setAllChecked(true); } }; selectButton.addSelectionListener(listener); } /** * Creates a DeSelect All button and its respective listener. * * @param buttonComposite */ private void createDeselectAll(Composite buttonComposite) { SelectionListener listener; Button deselectButton = createButton(buttonComposite, IDialogConstants.DESELECT_ALL_ID, DESELECT_ALL_TITLE, false); listener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { listViewer.setAllChecked(false); TableItem[] currentItems = listViewer.getTable().getItems(); for (TableItem tableItem : currentItems) { removeFromSelectedElements(tableItem.getText()); } } }; deselectButton.addSelectionListener(listener); } @Override protected Control createDialogArea(Composite parent) { // page group Composite composite = (Composite) super.createDialogArea(parent); initializeDialogUnits(composite); createMessageArea(composite); listViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER); GridData data = new GridData(GridData.FILL_BOTH); data.heightHint = SIZING_SELECTION_WIDGET_HEIGHT; data.widthHint = SIZING_SELECTION_WIDGET_WIDTH; listViewer.getTable().setLayoutData(data); listViewer.setLabelProvider(labelProvider); listViewer.setContentProvider(contentProvider); createSelectionButtons(composite); initContent(); // initialize page if (!getInitialElementSelections().isEmpty()) { checkInitialSelections(); } Dialog.applyDialogFont(composite); getViewer().addFilter(new ViewerFilter() { public boolean select(Viewer viewer, Object parentElement, Object element) { if (getCheckBoxTableViewer().getChecked(element)) { addToSelectedElements(element); } return matchExceptionToShowInList(element); } }); getCheckBoxTableViewer().addCheckStateListener(new ICheckStateListener() { public void checkStateChanged(CheckStateChangedEvent event) { if (event.getChecked()) { addToSelectedElements(event.getElement()); } else { removeFromSelectedElements(event.getElement()); } } }); createCustomExceptionUI(composite); createCaughtUncaughtCheck(composite); return composite; } /** * @param composite * * Create a new text box and a button, which allows user to add * custom exception. Attach a listener to the AddException Button */ private void createCustomExceptionUI(Composite composite) { addNewExceptionField = new Text(composite, SWT.BORDER); addNewExceptionField.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false)); Button buttonAdd = new Button(composite, SWT.PUSH); buttonAdd.setLayoutData(new GridData(GridData.END, GridData.END, true, false)); buttonAdd.setText("Add Exception"); SelectionListener listener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { addCustomException(); } }; buttonAdd.addSelectionListener(listener); } /** * Add the new exception in the content pane * */ private void addCustomException() { String customException = addNewExceptionField.getText(); Object[] currentElements = contentProvider.getElements(inputElement); ArrayList<Object> currentElementsList = new ArrayList<Object>(); for (int i = 0; i < currentElements.length; ++i) { Object element = currentElements[i]; currentElementsList.add(element); } if (customException == "") return; if (!currentElementsList.contains(customException)) { getViewer().add(customException); addNewExceptionField.setText(""); ((PyExceptionListProvider) contentProvider).addUserConfiguredException(customException); } else { IStatus status = new Status(IStatus.WARNING, DebugUIPlugin.getUniqueIdentifier(), "Duplicate: This exception already exists"); DebugUIPlugin.errorDialog(getShell(), DebugUIPlugin.removeAccelerators("Add Custom User Exception"), "Error", status); } } /** * Creates two checkboxes to enable/disable breaking on the exception. * The default value for Suspend on caught exception is false * The default value for suspend on uncaught exception is false * * @param composite */ private void createCaughtUncaughtCheck(Composite composite) { PyExceptionBreakPointManager instance = PyExceptionBreakPointManager.getInstance(); String breakOnCaught = instance.getBreakOnCaughtExceptions(); String breakOnUncaught = instance.getBreakOnUncaughtExceptions(); uncaughtExceptionCheck = new Button(composite, SWT.CHECK); uncaughtExceptionCheck.setText("Suspend on uncaught exceptions"); if (breakOnUncaught.length() > 0) { uncaughtExceptionCheck.setSelection(Boolean.parseBoolean(breakOnUncaught)); } else { uncaughtExceptionCheck.setSelection(false); } caughtExceptionCheck = new Button(composite, SWT.CHECK); caughtExceptionCheck.setText("Suspend on caught exceptions *"); if (breakOnCaught.length() > 0) { caughtExceptionCheck.setSelection(Boolean.parseBoolean(breakOnCaught)); } else { caughtExceptionCheck.setSelection(false); } Label label = new Label(composite, SWT.NONE); label.setText("* Will make debugging ~ 2x slower"); } /** * Updates the current filter with the text field text. */ protected void doFilterUpdate(IProgressMonitor monitor) { setFilter(filterPatternField.getText(), monitor, true); } // filtering things... protected void setFilter(String text, IProgressMonitor monitor, boolean updateFilterMatcher) { if (monitor.isCanceled()) return; if (updateFilterMatcher) { // just so that subclasses may already treat it. if (fFilterMatcher.lastPattern.equals(text)) { // no actual change... return; } fFilterMatcher.setFilter(text); if (monitor.isCanceled()) return; } getViewer().refresh(); setSelectedElementChecked(); } protected boolean matchExceptionToShowInList(Object element) { return fFilterMatcher.match(element); } /** * The <code>ListSelectionDialog</code> implementation of this * <code>Dialog</code> method builds a list of the selected elements for * later retrieval by the client and closes this dialog. */ protected void okPressed() { // Get the input children. Object[] children = contentProvider.getElements(inputElement); // Build a list of selected children. if (children != null) { ArrayList<Object> list = new ArrayList<Object>(); for (int i = 0; i < children.length; ++i) { Object element = children[i]; if (listViewer.getChecked(element)) { list.add(element); } } // If filter is on and checkedElements are not in filtered list // then content provider.getElements doesn't fetch the same if (selectedElements != null) { for (Object selectedElement : selectedElements) { if (!list.contains(selectedElement)) { list.add(selectedElement); } } } setResult(list); } //Save whether to break debugger or not on caught / uncaught exceptions handleCaughtExceptions = caughtExceptionCheck.getSelection(); handleUncaughtExceptions = uncaughtExceptionCheck.getSelection(); super.okPressed(); } public boolean getResultHandleUncaughtExceptions() { return this.handleUncaughtExceptions; } public boolean getResultHandleCaughtExceptions() { return this.handleCaughtExceptions; } /** * Returns the viewer used to show the list. * * @return the viewer, or <code>null</code> if not yet created */ protected CheckboxTableViewer getViewer() { return listViewer; } /** * Returns the viewer cast to the correct instance. Possibly * <code>null</code> if the viewer has not been created yet. * * @return the viewer cast to CheckboxTableViewer */ protected CheckboxTableViewer getCheckBoxTableViewer() { return (CheckboxTableViewer) getViewer(); } /** * Initialises this dialog's viewer after it has been laid out. */ private void initContent() { listViewer.setInput(inputElement); Listener listener = new Listener() { public void handleEvent(Event e) { if (updateInThread) { if (filterJob != null) { // cancel it if it was already in progress filterJob.cancel(); } filterJob = new FilterJob(); filterJob.start(); } else { doFilterUpdate(new NullProgressMonitor()); } } }; filterPatternField.setText(filterPattern != null ? filterPattern : ""); filterPatternField.addListener(SWT.Modify, listener); } /** * Visually checks the previously-specified elements in this dialog's list * viewer. */ private void checkInitialSelections() { Iterator itemsToCheck = getInitialElementSelections().iterator(); while (itemsToCheck.hasNext()) { listViewer.setChecked(itemsToCheck.next(), true); } } /** * setSelectedElementChecked * * Visually checks the elements in the selectedElements list after the * refresh, which is triggered on applying / removing filter * */ private void setSelectedElementChecked() { if (selectedElements != null) { for (Object element : selectedElements) { getViewer().setChecked(element, true); } } } private List<Object> selectedElements; private void addToSelectedElements(Object element) { if (selectedElements == null) selectedElements = new ArrayList<Object>(); if (!selectedElements.contains(element)) selectedElements.add(element); } private void removeFromSelectedElements(Object element) { if (selectedElements != null && selectedElements.contains(element)) selectedElements.remove(element); } class FilterJob extends Thread { // only thing it implements is the cancelled IProgressMonitor monitor = new NullProgressMonitor(); public FilterJob() { setPriority(Thread.MIN_PRIORITY); setName("PyConfigureExceptionDialog: FilterJob"); } @Override public void run() { try { sleep(300); } catch (InterruptedException e) { // ignore } if (!monitor.isCanceled()) { Display display = Display.getDefault(); display.asyncExec(new Runnable() { public void run() { if (!monitor.isCanceled() && filterPatternField != null && !filterPatternField.isDisposed()) { doFilterUpdate(monitor); } } }); } } public void cancel() { this.monitor.setCanceled(true); } } protected class DefaultFilterMatcher { public StringMatcher fMatcher; public String lastPattern; public DefaultFilterMatcher() { setFilter(""); } public void setFilter(String pattern) { setFilter(pattern, true, false); } private void setFilter(String pattern, boolean ignoreCase, boolean ignoreWildCards) { fMatcher = new StringMatcher(pattern + '*', ignoreCase, ignoreWildCards); this.lastPattern = pattern; } public boolean match(Object element) { boolean match = fMatcher.match(labelProvider.getText(element)); if (match) { return true; } return false; } } }