Java tutorial
/******************************************************************************* * Copyright (c) 2008, 2010 SAP AG 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: * SAP AG - initial API and implementation *******************************************************************************/ package org.eclipse.mat.ui.internal.browser; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.PopupDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.resource.FontDescriptor; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.mat.SnapshotException; import org.eclipse.mat.query.IQueryContext; import org.eclipse.mat.query.registry.ArgumentSet; import org.eclipse.mat.query.registry.CategoryDescriptor; import org.eclipse.mat.query.registry.CommandLine; import org.eclipse.mat.query.registry.QueryDescriptor; import org.eclipse.mat.query.registry.QueryRegistry; import org.eclipse.mat.snapshot.ISnapshot; import org.eclipse.mat.ui.MemoryAnalyserPlugin; import org.eclipse.mat.ui.Messages; import org.eclipse.mat.ui.QueryExecution; import org.eclipse.mat.ui.accessibility.AccessibleCompositeAdapter; import org.eclipse.mat.ui.editor.MultiPaneEditor; import org.eclipse.mat.ui.util.ErrorHelper; import org.eclipse.mat.ui.util.IPolicy; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; 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.Font; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.graphics.TextStyle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; public class QueryBrowserPopup extends PopupDialog { public interface Element { String getLabel(); String getUsage(); QueryDescriptor getQuery(); ImageDescriptor getImageDescriptor(); void execute(MultiPaneEditor editor) throws SnapshotException; } private static final int INITIAL_COUNT_PER_PROVIDER = 4; private LocalResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources()); private List<QueryBrowserProvider> providers; private MultiPaneEditor editor; private IPolicy policy; private Text filterText; private QueryContextHelp helpText; private Table table; private TextLayout textLayout; private boolean resized = false; public QueryBrowserPopup(MultiPaneEditor editor) { this(editor, false); } public QueryBrowserPopup(MultiPaneEditor editor, boolean onlyHistory) { this(editor, onlyHistory, new Policy()); } public QueryBrowserPopup(MultiPaneEditor editor, boolean onlyHistory, org.eclipse.mat.ui.util.IPolicy policy) { super(editor.getEditorSite().getShell(), SWT.RESIZE, true, true, true, true, null, Messages.QueryBrowserPopup_StartTyping); this.editor = editor; this.policy = policy; QueryBrowserPopup.this.providers = new ArrayList<QueryBrowserProvider>(); providers.add(new QueryHistoryProvider(editor.getQueryContext(), policy)); if (!onlyHistory) { addCategories(QueryRegistry.instance().getRootCategory(), policy); providers.add(new QueryRegistryProvider(editor.getQueryContext(), QueryRegistry.instance().getRootCategory(), policy)); } create(); } private void addCategories(CategoryDescriptor category, IPolicy policy) { for (CategoryDescriptor c : category.getSubCategories()) { providers.add(new QueryRegistryProvider(editor.getQueryContext(), c, policy)); addCategories(c, policy); } } protected Control createTitleControl(Composite parent) { filterText = new Text(parent, SWT.NONE); GC gc = new GC(parent); gc.setFont(parent.getFont()); FontMetrics fontMetrics = gc.getFontMetrics(); gc.dispose(); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false) .hint(SWT.DEFAULT, Dialog.convertHeightInCharsToPixels(fontMetrics, 1)).applyTo(filterText); filterText.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { if (e.keyCode == 0x0D) { if ((e.stateMask & SWT.MOD1) != 0) handleSelection(true); else if (filterText.getText().length() == 0) handleSelection(false); else executeFilterText((e.stateMask & SWT.MOD2) == 0); return; } else if (e.keyCode == SWT.ARROW_DOWN) { int index = table.getSelectionIndex(); if (index != -1 && table.getItemCount() > index + 1) { table.setSelection(index + 1); updateHelp(); } table.setFocus(); } else if (e.keyCode == SWT.ARROW_UP) { int index = table.getSelectionIndex(); if (index != -1 && index >= 1) { table.setSelection(index - 1); updateHelp(); table.setFocus(); } } else if (e.character == 0x1B) // ESC close(); } public void keyReleased(KeyEvent e) { } }); filterText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { String text = ((Text) e.widget).getText().toLowerCase(); refresh(text); } }); return filterText; } protected Control createDialogArea(Composite parent) { Composite composite = (Composite) super.createDialogArea(parent); boolean isWin32 = "win32".equals(SWT.getPlatform()); //$NON-NLS-1$ GridLayoutFactory.fillDefaults().extendedMargins(isWin32 ? 0 : 3, 3, 2, 2).applyTo(composite); Composite tableComposite = new Composite(composite, SWT.NONE); GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite); TableColumnLayout tableColumnLayout = new TableColumnLayout(); tableComposite.setLayout(tableColumnLayout); table = new Table(tableComposite, SWT.SINGLE | SWT.FULL_SELECTION); textLayout = new TextLayout(table.getDisplay()); textLayout.setOrientation(getDefaultOrientation()); Font boldFont = resourceManager.createFont(FontDescriptor.createFrom(table.getFont()).setStyle(SWT.BOLD)); textLayout.setFont(table.getFont()); textLayout.setText(Messages.QueryBrowserPopup_Categories); textLayout.setFont(boldFont); AccessibleCompositeAdapter.access(table); tableColumnLayout.setColumnData(new TableColumn(table, SWT.NONE), new ColumnWeightData(100, 100)); table.getShell().addControlListener(new ControlAdapter() { public void controlResized(ControlEvent e) { if (!resized) { resized = true; e.display.timerExec(100, new Runnable() { public void run() { if (getShell() != null && !getShell().isDisposed()) { refresh(filterText.getText().toLowerCase()); } resized = false; } }); } } }); table.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { if (e.keyCode == SWT.ARROW_UP && table.getSelectionIndex() == 0) { filterText.setFocus(); } else if (e.character == SWT.ESC) { close(); } else if (e.keyCode == 0x0D && e.stateMask != 0) { handleSelection(true); } } public void keyReleased(KeyEvent e) { // do nothing } }); table.addMouseListener(new MouseAdapter() { @Override public void mouseUp(MouseEvent e) { if (table.getSelectionCount() < 1) return; if (e.button != 1 && e.button != 3) return; if (table.equals(e.getSource())) { Object o = table.getItem(new Point(e.x, e.y)); TableItem selection = table.getSelection()[0]; if (selection.equals(o)) { if (e.button != 1) { handleSelection(true); } } } } }); table.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { updateHelp(); } public void widgetDefaultSelected(SelectionEvent e) { handleSelection(false); } }); final TextStyle boldStyle = new TextStyle(boldFont, null, null); Listener listener = new Listener() { public void handleEvent(Event event) { QueryBrowserItem entry = (QueryBrowserItem) event.item.getData(); if (entry != null) { switch (event.type) { case SWT.MeasureItem: entry.measure(event, textLayout, resourceManager, boldStyle); break; case SWT.PaintItem: entry.paint(event, textLayout, resourceManager, boldStyle); break; case SWT.EraseItem: entry.erase(event); break; } } else { switch (event.type) { case SWT.MeasureItem: event.height = Math.max(event.height, 16 + 2); event.width = 16; break; case SWT.PaintItem: break; case SWT.EraseItem: break; } } } }; table.addListener(SWT.MeasureItem, listener); table.addListener(SWT.EraseItem, listener); table.addListener(SWT.PaintItem, listener); // unless one table item is created (and measured) the wrong item height // is returned. Therefore more elements are displayed than space is // available and - oops - ugly scroll bars appear new TableItem(table, SWT.NONE); return composite; } private int computeNumberOfItems() { int height = table.getClientArea().height; int lineWidth = table.getLinesVisible() ? table.getGridLineWidth() : 0; return (height - lineWidth) / (table.getItemHeight() + lineWidth); } private void refresh(String filter) { int numItems = computeNumberOfItems(); List<QueryBrowserItem> entries = computeMatchingEntries(filter, numItems); refreshTable(entries); if (table.getItemCount() > 0) { table.setSelection(0); updateHelp(); } if (filter.length() == 0) setInfoText(Messages.QueryBrowserPopup_StartTyping); else setInfoText(Messages.QueryBrowserPopup_PressCtrlEnter); } protected Control getFocusControl() { return filterText; } public boolean close() { if (textLayout != null && !textLayout.isDisposed()) { textLayout.dispose(); } if (helpText != null) { helpText.close(); helpText = null; } if (resourceManager != null) { resourceManager.dispose(); resourceManager = null; } return super.close(); } protected Point getInitialSize() { if (!getPersistBounds()) return new Point(450, 400); return super.getInitialSize(); } protected Point getInitialLocation(Point initialSize) { if (!getPersistBounds()) { Point size = new Point(400, 400); Rectangle parentBounds = getParentShell().getBounds(); int x = parentBounds.x + parentBounds.width / 2 - size.x / 2; int y = parentBounds.y + parentBounds.height / 2 - size.y / 2; return new Point(x, y); } return super.getInitialLocation(initialSize); } protected IDialogSettings getDialogSettings() { final IDialogSettings workbenchDialogSettings = MemoryAnalyserPlugin.getDefault().getDialogSettings(); IDialogSettings result = workbenchDialogSettings.getSection(QueryBrowserPopup.class.getName()); if (result == null) { result = workbenchDialogSettings.addNewSection(QueryBrowserPopup.class.getName()); } return result; } @Override protected void fillDialogMenu(IMenuManager dialogMenu) { dialogMenu.add(new Action(Messages.MultiPaneEditor_Close) { @Override public void run() { close(); } }); dialogMenu.add(new Separator()); super.fillDialogMenu(dialogMenu); } private void executeFilterText(boolean prompt) { try { String cmdLine = filterText.getText(); close(); IQueryContext context = editor.getQueryContext(); ArgumentSet argumentSet = CommandLine.parse(context, cmdLine); boolean reproducable = true; // Used to use argumentSet.isExecutable() but it it nice to fill in optional arguments from the context // For example find_strings from the query browser if (argumentSet.getUnsetArguments().size() > 0 && context.available(ISnapshot.class, null)) { // Fill in some missing arguments from the policy ISnapshot snapshot = (ISnapshot) context.get(ISnapshot.class, null); QueryDescriptor query = argumentSet.getQueryDescriptor(); String before = argumentSet.toString(); policy.fillInObjectArguments(snapshot, query, argumentSet); String after = argumentSet.toString(); // See if the arguments have changed reproducable = before.equals(after); } QueryExecution.execute(editor, null, null, argumentSet, prompt, reproducable); } catch (SnapshotException e) { ErrorHelper.showErrorMessage(e); } } private void handleSelection(boolean doUpdateFilterText) { if (table.getSelectionCount() != 1) { close(); return; } QueryBrowserItem queryBrowserItem = ((QueryBrowserItem) table.getItem(table.getSelectionIndex()).getData()); Element selectedElement = queryBrowserItem.element; if (selectedElement == null) { // Category, so select just this category String name = queryBrowserItem.provider.getName(); filterText.setText(name); filterText.setSelection(0); filterText.setFocus(); return; } if (doUpdateFilterText) { filterText.setText(selectedElement.getUsage()); filterText.setSelection(0); filterText.setFocus(); } else { close(); if (!editor.isDisposed()) { try { selectedElement.execute(editor); } catch (SnapshotException e) { ErrorHelper.showErrorMessage(e); } } } } private void updateHelp() { if (table.getSelectionCount() > 0) { Element selectedElement = ((QueryBrowserItem) table.getItem(table.getSelectionIndex()) .getData()).element; QueryDescriptor query = selectedElement != null ? selectedElement.getQuery() : null; if (query != null) { // At least display the usage even if no help is available. if (helpText == null || helpText.getQuery() != query) { if (helpText != null) helpText.close(); Rectangle myBounds = getShell().getBounds(); Rectangle helpBounds = new Rectangle(myBounds.x, myBounds.y + myBounds.height, myBounds.width, SWT.DEFAULT); helpText = new QueryContextHelp(getShell(), query, editor.getQueryContext(), helpBounds); helpText.open(); } } else { if (helpText != null) { helpText.close(); helpText = null; } } } else { if (helpText != null) { helpText.close(); helpText = null; } } } private void refreshTable(List<QueryBrowserItem> entries) { if (table.getItemCount() > entries.size() && table.getItemCount() - entries.size() > 20) { table.removeAll(); } TableItem[] items = table.getItems(); int index = 0; for (QueryBrowserItem entry : entries) { TableItem item; if (index < items.length) { item = items[index]; table.clear(index); } else { item = new TableItem(table, SWT.NONE); } item.setData(entry); if (entry.element != null) item.setText(0, entry.element.getLabel()); else item.setText(0, entry.provider.getName()); index++; } if (index < items.length) { table.remove(index, items.length - 1); } } private List<QueryBrowserItem> computeMatchingEntries(String filter, int maxCount) { // first: collect entries on a category level (distribute search // results...) List<List<QueryBrowserItem>> entries = new ArrayList<List<QueryBrowserItem>>(providers.size()); // For all except the initial page, list all the queries and let the receiver scroll boolean noLimits = filter.length() > 0; // Allow for extra entry to select all if (!noLimits) maxCount--; if (filter.equals(QueryAllProvider.ALL.toLowerCase())) filter = ""; //$NON-NLS-1$ int[] indexPerCategory = new int[providers.size()]; int countPerCategory = Math.min(maxCount / 4, INITIAL_COUNT_PER_PROVIDER); int countTotal = 0; boolean done = false; while ((countTotal < maxCount || noLimits) && !done) { done = true; for (int ii = 0; ii < providers.size() && (countTotal < maxCount || noLimits); ii++) { List<QueryBrowserItem> e = null; if (ii == entries.size()) { entries.add(e = new ArrayList<QueryBrowserItem>()); countTotal++; // categories are rendered on one line each } else { e = entries.get(ii); } int count = 0; QueryBrowserProvider provider = providers.get(ii); Element[] elements = provider.getElementsSorted(); int j = indexPerCategory[ii]; while (j < elements.length && (count < countPerCategory && countTotal < maxCount || noLimits)) { Element element = elements[j]; QueryBrowserItem entry = null; if (filter.length() == 0) { entry = new QueryBrowserItem(element, provider, 0, 0); } else { String sortLabel = element.getLabel(); int index = sortLabel.toLowerCase().indexOf(filter); if (index != -1) { entry = new QueryBrowserItem(element, provider, index, index + filter.length() - 1); } else { // test whether category or description or usage match index = provider.getName().toLowerCase().indexOf(filter); if (index == -1 && element.getQuery() != null) { String help = element.getQuery().getHelp(); if (help != null) index = help.toLowerCase().indexOf(filter); if (index == -1) { String usage = element.getUsage(); index = usage.toLowerCase().indexOf(filter); } } if (index != -1) entry = new QueryBrowserItem(element, provider, 0, 0); } } if (entry != null) { e.add(entry); count++; countTotal++; } j++; } indexPerCategory[ii] = j; if (j < elements.length) { done = false; } } countPerCategory = 1; } // second: convert 'em to a flat list for easy display List<QueryBrowserItem> answer = new ArrayList<QueryBrowserItem>(); for (List<QueryBrowserItem> items : entries) { if (!items.isEmpty()) { QueryBrowserItem firstElement = items.get(0); answer.add(new QueryBrowserItem(null, firstElement.provider, 0, 0)); answer.addAll(items); answer.get(answer.size() - 1).lastInCategory = true; } } // Add the option to display all queries if (!noLimits && maxCount >= 0) answer.add(new QueryBrowserItem(null, new QueryAllProvider(), 0, 0)); return answer; } }