package org.jbehave.eclipse.editor.story.outline; import java.util.regex.Pattern; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.PopupDialog; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; 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.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.jbehave.eclipse.editor.story.PatternViewFilter; import org.jbehave.eclipse.editor.story.StoryEditor; import org.jbehave.eclipse.editor.text.TextProvider; import org.jbehave.eclipse.util.Strings; public class QuickOutlinePopupDialog extends PopupDialog { private ImageRegistry imageRegistry; /** * The text control that displays the text. */ private TreeViewer treeViewer; private PatternViewFilter fNamePatternFilter; private QuickOutlineTreeContentProvider treeContentProvider; private QuickOutlineStyledLabelProvider labelProvider; private StoryEditor editor; private Text filterText; public QuickOutlinePopupDialog(Shell parent, int shellStyle, StoryEditor editor, ImageRegistry imageRegistry) { super(parent, shellStyle, true, true, true, true, true, null, null); setInfoText("select a value or press ESC to exit"); this.editor = editor; this.imageRegistry = imageRegistry; } public void setFocus() { getShell().forceFocus(); filterText.setFocus(); } @Override protected Control getFocusControl() { return filterText; } /* * Create a text control for showing the info about a proposal. */ protected Control createDialogArea(Composite parent) { /* * text = new Text(parent, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | * SWT.NO_FOCUS); text.setText(contents); return text; */ // Create the tree viewer createUIWidgetTreeViewer(parent); // Add listeners to the tree viewer createUIListenersTreeViewer(); // createUIActions(); // Return the tree return treeViewer.getControl(); } /** * */ private void createUIActions() { } /** * @param parent */ private void createUIWidgetTreeViewer(final Composite parent) { final int style = SWT.H_SCROLL | SWT.V_SCROLL; // Create the tree final Tree widget = new Tree(parent, style); // Configure the layout final GridData data = new GridData(GridData.FILL_BOTH); data.heightHint = widget.getItemHeight() * 12; widget.setLayoutData(data); // Create the tree viewer treeViewer = new TreeViewer(widget); // Add the name pattern filter fNamePatternFilter = new PatternViewFilter(); treeViewer.addFilter(fNamePatternFilter); // Set the content provider treeContentProvider = new QuickOutlineTreeContentProvider(); treeViewer.setContentProvider(treeContentProvider); // Set the label provider labelProvider = new QuickOutlineStyledLabelProvider(imageRegistry); treeViewer.setLabelProvider(labelProvider); // Create the outline sorter (to be set on the sort action) // fTreeViewerComparator = fOutlineContentCreator // .createOutlineComparator(); // Set the comparator to null (sort action will be disabled initially // because of this) // Create the default outline sorter (Most like this will just return // null to indicate sorting disabled // fTreeViewerDefaultComparator = fOutlineContentCreator // .createDefaultOutlineComparator(); // fTreeViewer.setComparator(fTreeViewerDefaultComparator); treeViewer.setAutoExpandLevel(1); treeViewer.setUseHashlookup(true); treeViewer.setInput(editor.getOutlineModels()); } /** * */ private void createUIListenersTreeViewer() { // Get the underlying tree widget final Tree tree = treeViewer.getTree(); // Handle key events tree.addKeyListener(new KeyListener() { public void keyPressed(final KeyEvent e) { if (e.character == 0x1B) { // Dispose on ESC key press close(); } } public void keyReleased(final KeyEvent e) { // NO-OP } }); // Handle mouse clicks tree.addMouseListener(new MouseAdapter() { @Override public void mouseDoubleClick(MouseEvent e) { gotoSelectedElement(); } }); // Handle widget selection events tree.addSelectionListener(new SelectionListener() { public void widgetSelected(final SelectionEvent e) { // NO-OP } public void widgetDefaultSelected(final SelectionEvent e) { gotoSelectedElement(); } }); } /** * @return */ private Object getSelectedElement() { if (treeViewer == null) { return null; } return ((IStructuredSelection) treeViewer.getSelection()) .getFirstElement(); } @Override protected Control createTitleControl(final Composite parent) { // Applies only to dialog title - not body. See createDialogArea // Create the text widget createUIWidgetFilterText(parent); // Add listeners to the text widget createUIListenersFilterText(); // Return the text widget return filterText; } /** * @param parent * @return */ private void createUIWidgetFilterText(final Composite parent) { // Create the widget filterText = new Text(parent, SWT.NONE); // Set the font final GC gc = new GC(parent); gc.setFont(parent.getFont()); final FontMetrics fontMetrics = gc.getFontMetrics(); gc.dispose(); // Create the layout final GridData data = new GridData(GridData.FILL_HORIZONTAL); data.heightHint = Dialog.convertHeightInCharsToPixels(fontMetrics, 1); data.horizontalAlignment = GridData.FILL; data.verticalAlignment = GridData.CENTER; filterText.setLayoutData(data); } /** * */ private void createUIListenersFilterText() { // Handle key events filterText.addKeyListener(new KeyListener() { public void keyPressed(final KeyEvent e) { if (e.keyCode == 0x0D) { // Return key was pressed gotoSelectedElement(); } else if (e.keyCode == SWT.ARROW_DOWN) { // Down key was pressed treeViewer.getTree().setFocus(); } else if (e.keyCode == SWT.ARROW_UP) { // Up key was pressed treeViewer.getTree().setFocus(); } else if (e.character == 0x1B) { // Escape key was pressed close(); } } public void keyReleased(final KeyEvent e) { // NO-OP } }); // Handle text modify events filterText.addModifyListener(new ModifyListener() { public void modifyText(final ModifyEvent e) { String text = ((Text) e.widget).getText(); final int length = text.length(); if (length > 0) { // Append a '*' pattern to the end of the text value if it // does not have one already if (text.charAt(length - 1) != '*') { text = text + '*'; } // Prepend a '*' pattern to the beginning of the text value // if it does not have one already if (text.charAt(0) != '*') { text = '*' + text; } } // Set and update the pattern setMatcherString(text, true); } }); } private void gotoSelectedElement() { OutlineModel selectedElement = (OutlineModel)getSelectedElement(); editor.showRange(selectedElement.getOffset(), selectedElement.getLength()); close(); } private Pattern namePattern; /** * Sets the patterns to filter out for the receiver. * <p> * The following characters have special meaning: ? => any character * => * any string * </p> * * @param pattern * the pattern * @param update * <code>true</code> if the viewer should be updated */ void setMatcherString(final String pattern, final boolean update) { if (pattern.length() == 0) { namePattern = null; } else { namePattern = Strings.convertGlobToPattern(pattern); } // Update the name pattern filter on the tree viewer fNamePatternFilter.setPattern(namePattern); // Update the tree viewer according to the pattern if (update) { namePatternUpdated(); } } /** * The string matcher has been modified. The default implementation * refreshes the view and selects the first matched element */ private void namePatternUpdated() { // Refresh the tree viewer to re-filter treeViewer.getControl().setRedraw(false); treeViewer.refresh(); treeViewer.expandAll(); selectFirstMatch(); treeViewer.getControl().setRedraw(true); } /** * Selects the first element in the tree which matches the current filter * pattern. */ private void selectFirstMatch() { final Tree tree = treeViewer.getTree(); final Object element = findFirstMatchToPattern(tree.getItems()); if (element != null) { treeViewer.setSelection(new StructuredSelection(element), true); } else { treeViewer.setSelection(StructuredSelection.EMPTY); } } /** * @param items * @return */ private Object findFirstMatchToPattern(final TreeItem[] items) { // Match the string pattern against labels final TextProvider textProvider = (TextProvider) treeViewer.getLabelProvider(); // Process each item in the tree for (int i = 0; i < items.length; i++) { Object element = items[i].getData(); // Return the first element if no pattern is set if (namePattern == null) { return element; } // Return the element if it matches the pattern if (element != null) { final String label = textProvider.textOf(element); if (namePattern.matcher(label).matches()) { return element; } } // Recursively check the elements children for a match element = findFirstMatchToPattern(items[i].getItems()); // Return the child element match if found if (element != null) { return element; } } // No match found return null; } }