| 1 | /******************************************************************************* |
| 2 | * Copyright (c) 2005, 2008 IBM Corporation and others. |
| 3 | * All rights reserved. This program and the accompanying materials |
| 4 | * are made available under the terms of the Eclipse Public License v1.0 |
| 5 | * which accompanies this distribution, and is available at |
| 6 | * http://www.eclipse.org/legal/epl-v10.html |
| 7 | * |
| 8 | * Contributors: |
| 9 | * IBM Corporation - initial API and implementation |
| 10 | *******************************************************************************/ |
| 11 | package org.eclipse.ui.internal.quickaccess; |
| 12 | |
| 13 | import java.util.ArrayList; |
| 14 | import java.util.HashMap; |
| 15 | import java.util.Iterator; |
| 16 | import java.util.LinkedList; |
| 17 | import java.util.List; |
| 18 | import java.util.Map; |
| 19 | |
| 20 | import org.eclipse.core.commands.Command; |
| 21 | import org.eclipse.core.runtime.Assert; |
| 22 | import org.eclipse.jface.bindings.TriggerSequence; |
| 23 | import org.eclipse.jface.bindings.keys.KeySequence; |
| 24 | import org.eclipse.jface.bindings.keys.SWTKeySupport; |
| 25 | import org.eclipse.jface.dialogs.IDialogSettings; |
| 26 | import org.eclipse.jface.dialogs.PopupDialog; |
| 27 | import org.eclipse.jface.layout.GridDataFactory; |
| 28 | import org.eclipse.jface.layout.GridLayoutFactory; |
| 29 | import org.eclipse.jface.layout.TableColumnLayout; |
| 30 | import org.eclipse.jface.resource.FontDescriptor; |
| 31 | import org.eclipse.jface.resource.ImageDescriptor; |
| 32 | import org.eclipse.jface.resource.JFaceResources; |
| 33 | import org.eclipse.jface.resource.LocalResourceManager; |
| 34 | import org.eclipse.jface.viewers.ColumnWeightData; |
| 35 | import org.eclipse.osgi.util.NLS; |
| 36 | import org.eclipse.swt.SWT; |
| 37 | import org.eclipse.swt.custom.BusyIndicator; |
| 38 | import org.eclipse.swt.events.ControlAdapter; |
| 39 | import org.eclipse.swt.events.ControlEvent; |
| 40 | import org.eclipse.swt.events.KeyAdapter; |
| 41 | import org.eclipse.swt.events.KeyEvent; |
| 42 | import org.eclipse.swt.events.KeyListener; |
| 43 | import org.eclipse.swt.events.ModifyEvent; |
| 44 | import org.eclipse.swt.events.ModifyListener; |
| 45 | import org.eclipse.swt.events.MouseAdapter; |
| 46 | import org.eclipse.swt.events.MouseEvent; |
| 47 | import org.eclipse.swt.events.SelectionEvent; |
| 48 | import org.eclipse.swt.events.SelectionListener; |
| 49 | import org.eclipse.swt.graphics.Color; |
| 50 | import org.eclipse.swt.graphics.Font; |
| 51 | import org.eclipse.swt.graphics.Point; |
| 52 | import org.eclipse.swt.graphics.Rectangle; |
| 53 | import org.eclipse.swt.graphics.TextLayout; |
| 54 | import org.eclipse.swt.graphics.TextStyle; |
| 55 | import org.eclipse.swt.widgets.Composite; |
| 56 | import org.eclipse.swt.widgets.Control; |
| 57 | import org.eclipse.swt.widgets.Event; |
| 58 | import org.eclipse.swt.widgets.Listener; |
| 59 | import org.eclipse.swt.widgets.Shell; |
| 60 | import org.eclipse.swt.widgets.Table; |
| 61 | import org.eclipse.swt.widgets.TableColumn; |
| 62 | import org.eclipse.swt.widgets.TableItem; |
| 63 | import org.eclipse.swt.widgets.Text; |
| 64 | import org.eclipse.ui.IWorkbenchPage; |
| 65 | import org.eclipse.ui.IWorkbenchWindow; |
| 66 | import org.eclipse.ui.internal.IWorkbenchGraphicConstants; |
| 67 | import org.eclipse.ui.internal.WorkbenchImages; |
| 68 | import org.eclipse.ui.internal.WorkbenchPlugin; |
| 69 | import org.eclipse.ui.internal.progress.ProgressManagerUtil; |
| 70 | import org.eclipse.ui.keys.IBindingService; |
| 71 | import org.eclipse.ui.themes.ColorUtil; |
| 72 | |
| 73 | |
| 74 | /** |
| 75 | * @since 3.3 |
| 76 | * |
| 77 | */ |
| 78 | public class QuickAccessDialog extends PopupDialog { |
| 79 | private static final int INITIAL_COUNT_PER_PROVIDER = 5; |
| 80 | private static final int MAX_COUNT_TOTAL = 20; |
| 81 | |
| 82 | private Text filterText; |
| 83 | |
| 84 | private QuickAccessProvider[] providers; |
| 85 | private IWorkbenchWindow window; |
| 86 | |
| 87 | private Table table; |
| 88 | |
| 89 | private LocalResourceManager resourceManager = new LocalResourceManager( |
| 90 | JFaceResources.getResources()); |
| 91 | |
| 92 | private static final String TEXT_ARRAY = "textArray"; //$NON-NLS-1$ |
| 93 | private static final String TEXT_ENTRIES = "textEntries"; //$NON-NLS-1$ |
| 94 | private static final String ORDERED_PROVIDERS = "orderedProviders"; //$NON-NLS-1$ |
| 95 | private static final String ORDERED_ELEMENTS = "orderedElements"; //$NON-NLS-1$ |
| 96 | static final int MAXIMUM_NUMBER_OF_ELEMENTS = 60; |
| 97 | static final int MAXIMUM_NUMBER_OF_TEXT_ENTRIES_PER_ELEMENT = 3; |
| 98 | |
| 99 | protected String rememberedText; |
| 100 | |
| 101 | protected Map textMap = new HashMap(); |
| 102 | |
| 103 | protected Map elementMap = new HashMap(); |
| 104 | |
| 105 | private LinkedList previousPicksList = new LinkedList(); |
| 106 | |
| 107 | protected Map providerMap; |
| 108 | // private Font italicsFont; |
| 109 | private Color grayColor; |
| 110 | private TextLayout textLayout; |
| 111 | private TriggerSequence[] invokingCommandKeySequences; |
| 112 | private Command invokingCommand; |
| 113 | private KeyAdapter keyAdapter; |
| 114 | private boolean showAllMatches = false; |
| 115 | protected boolean resized = false; |
| 116 | |
| 117 | |
| 118 | QuickAccessDialog(IWorkbenchWindow window, final Command invokingCommand) { |
| 119 | super(ProgressManagerUtil.getDefaultParent(), SWT.RESIZE, true, true, // persist size |
| 120 | false, // but not location |
| 121 | true, true, null, |
| 122 | QuickAccessMessages.QuickAccess_StartTypingToFindMatches); |
| 123 | |
| 124 | this.window = window; |
| 125 | BusyIndicator.showWhile(window.getShell() == null ? null : window |
| 126 | .getShell().getDisplay(), new Runnable() { |
| 127 | public void run() { |
| 128 | QuickAccessDialog.this.providers = new QuickAccessProvider[] { |
| 129 | new PreviousPicksProvider(), new EditorProvider(), |
| 130 | new ViewProvider(), new PerspectiveProvider(), |
| 131 | new CommandProvider(), new ActionProvider(), |
| 132 | new WizardProvider(), new PreferenceProvider(), |
| 133 | new PropertiesProvider() }; |
| 134 | providerMap = new HashMap(); |
| 135 | for (int i = 0; i < providers.length; i++) { |
| 136 | providerMap.put(providers[i].getId(), providers[i]); |
| 137 | } |
| 138 | restoreDialog(); |
| 139 | QuickAccessDialog.this.invokingCommand = invokingCommand; |
| 140 | if (QuickAccessDialog.this.invokingCommand != null |
| 141 | && !QuickAccessDialog.this.invokingCommand.isDefined()) { |
| 142 | QuickAccessDialog.this.invokingCommand = null; |
| 143 | } else { |
| 144 | // Pre-fetch key sequence - do not change because scope will |
| 145 | // change later. |
| 146 | getInvokingCommandKeySequences(); |
| 147 | } |
| 148 | // create early |
| 149 | create(); |
| 150 | } |
| 151 | }); |
| 152 | // Ugly hack to avoid bug 184045. If this gets fixed, replace the |
| 153 | // following code with a call to refresh(""). |
| 154 | getShell().getDisplay().asyncExec(new Runnable() { |
| 155 | public void run() { |
| 156 | final Shell shell = getShell(); |
| 157 | if (shell != null && !shell.isDisposed()) { |
| 158 | Point size = shell.getSize(); |
| 159 | shell.setSize(size.x, size.y + 1); |
| 160 | } |
| 161 | } |
| 162 | }); |
| 163 | } |
| 164 | |
| 165 | protected Control createTitleControl(Composite parent) { |
| 166 | filterText = new Text(parent, SWT.NONE); |
| 167 | |
| 168 | GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false) |
| 169 | .applyTo(filterText); |
| 170 | |
| 171 | filterText.addKeyListener(getKeyAdapter()); |
| 172 | filterText.addKeyListener(new KeyListener() { |
| 173 | public void keyPressed(KeyEvent e) { |
| 174 | if (e.keyCode == 0x0D) { |
| 175 | handleSelection(); |
| 176 | return; |
| 177 | } else if (e.keyCode == SWT.ARROW_DOWN) { |
| 178 | int index = table.getSelectionIndex(); |
| 179 | if (index != -1 && table.getItemCount() > index + 1) { |
| 180 | table.setSelection(index + 1); |
| 181 | } |
| 182 | table.setFocus(); |
| 183 | } else if (e.keyCode == SWT.ARROW_UP) { |
| 184 | int index = table.getSelectionIndex(); |
| 185 | if (index != -1 && index >= 1) { |
| 186 | table.setSelection(index - 1); |
| 187 | table.setFocus(); |
| 188 | } |
| 189 | } else if (e.character == 0x1B) // ESC |
| 190 | close(); |
| 191 | } |
| 192 | |
| 193 | public void keyReleased(KeyEvent e) { |
| 194 | // do nothing |
| 195 | } |
| 196 | }); |
| 197 | filterText.addModifyListener(new ModifyListener() { |
| 198 | public void modifyText(ModifyEvent e) { |
| 199 | String text = ((Text) e.widget).getText().toLowerCase(); |
| 200 | refresh(text); |
| 201 | } |
| 202 | }); |
| 203 | |
| 204 | return filterText; |
| 205 | } |
| 206 | |
| 207 | /* |
| 208 | * (non-Javadoc) |
| 209 | * |
| 210 | * @see org.eclipse.jface.dialogs.PopupDialog#createDialogArea(org.eclipse.swt.widgets.Composite) |
| 211 | */ |
| 212 | protected Control createDialogArea(Composite parent) { |
| 213 | Composite composite = (Composite) super.createDialogArea(parent); |
| 214 | boolean isWin32 = "win32".equals(SWT.getPlatform()); //$NON-NLS-1$ |
| 215 | GridLayoutFactory.fillDefaults().extendedMargins(isWin32 ? 0 : 3, 3, 2, 2).applyTo(composite); |
| 216 | Composite tableComposite = new Composite(composite, SWT.NONE); |
| 217 | GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite); |
| 218 | TableColumnLayout tableColumnLayout = new TableColumnLayout(); |
| 219 | tableComposite.setLayout(tableColumnLayout); |
| 220 | table = new Table(tableComposite, SWT.SINGLE | SWT.FULL_SELECTION); |
| 221 | textLayout = new TextLayout(table.getDisplay()); |
| 222 | textLayout.setOrientation(getDefaultOrientation()); |
| 223 | Font boldFont = resourceManager.createFont(FontDescriptor.createFrom( |
| 224 | JFaceResources.getDialogFont()).setStyle(SWT.BOLD)); |
| 225 | textLayout.setFont(table.getFont()); |
| 226 | textLayout.setText(QuickAccessMessages.QuickAccess_AvailableCategories); |
| 227 | int maxProviderWidth = (int) (textLayout.getBounds().width * 1.1); |
| 228 | textLayout.setFont(boldFont); |
| 229 | for (int i = 0; i < providers.length; i++) { |
| 230 | QuickAccessProvider provider = providers[i]; |
| 231 | textLayout.setText(provider.getName()); |
| 232 | int width = (int) (textLayout.getBounds().width * 1.1); |
| 233 | if (width > maxProviderWidth) { |
| 234 | maxProviderWidth = width; |
| 235 | } |
| 236 | } |
| 237 | tableColumnLayout.setColumnData(new TableColumn(table, SWT.NONE), |
| 238 | new ColumnWeightData(0, maxProviderWidth)); |
| 239 | tableColumnLayout.setColumnData(new TableColumn(table, SWT.NONE), |
| 240 | new ColumnWeightData(100, 100)); |
| 241 | table.getShell().addControlListener(new ControlAdapter() { |
| 242 | public void controlResized(ControlEvent e) { |
| 243 | if (!showAllMatches) { |
| 244 | if (!resized) { |
| 245 | resized = true; |
| 246 | e.display.timerExec(100, new Runnable() { |
| 247 | public void run() { |
| 248 | if (getShell() != null |
| 249 | && !getShell().isDisposed()) { |
| 250 | refresh(filterText.getText().toLowerCase()); |
| 251 | } |
| 252 | resized = false; |
| 253 | } |
| 254 | }); |
| 255 | } |
| 256 | } |
| 257 | } |
| 258 | }); |
| 259 | |
| 260 | table.addKeyListener(getKeyAdapter()); |
| 261 | table.addKeyListener(new KeyListener() { |
| 262 | public void keyPressed(KeyEvent e) { |
| 263 | if (e.keyCode == SWT.ARROW_UP && table.getSelectionIndex() == 0) { |
| 264 | filterText.setFocus(); |
| 265 | } else if (e.character == SWT.ESC) { |
| 266 | close(); |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | public void keyReleased(KeyEvent e) { |
| 271 | // do nothing |
| 272 | } |
| 273 | }); |
| 274 | table.addMouseListener(new MouseAdapter() { |
| 275 | public void mouseUp(MouseEvent e) { |
| 276 | |
| 277 | if (table.getSelectionCount() < 1) |
| 278 | return; |
| 279 | |
| 280 | if (e.button != 1) |
| 281 | return; |
| 282 | |
| 283 | if (table.equals(e.getSource())) { |
| 284 | Object o= table.getItem(new Point(e.x, e.y)); |
| 285 | TableItem selection= table.getSelection()[0]; |
| 286 | if (selection.equals(o)) |
| 287 | handleSelection(); |
| 288 | } |
| 289 | } |
| 290 | }); |
| 291 | |
| 292 | table.addSelectionListener(new SelectionListener() { |
| 293 | public void widgetSelected(SelectionEvent e) { |
| 294 | // do nothing |
| 295 | } |
| 296 | |
| 297 | public void widgetDefaultSelected(SelectionEvent e) { |
| 298 | handleSelection(); |
| 299 | } |
| 300 | }); |
| 301 | |
| 302 | // italicsFont = resourceManager.createFont(FontDescriptor.createFrom( |
| 303 | // table.getFont()).setStyle(SWT.ITALIC)); |
| 304 | grayColor = resourceManager.createColor(ColorUtil.blend(table |
| 305 | .getBackground().getRGB(), table.getForeground().getRGB())); |
| 306 | final TextStyle boldStyle = new TextStyle(boldFont, null, null); |
| 307 | Listener listener = new Listener() { |
| 308 | public void handleEvent(Event event) { |
| 309 | QuickAccessEntry entry = (QuickAccessEntry) event.item |
| 310 | .getData(); |
| 311 | if (entry != null) { |
| 312 | switch (event.type) { |
| 313 | case SWT.MeasureItem: |
| 314 | entry.measure(event, textLayout, resourceManager, |
| 315 | boldStyle); |
| 316 | break; |
| 317 | case SWT.PaintItem: |
| 318 | entry.paint(event, textLayout, resourceManager, |
| 319 | boldStyle, grayColor); |
| 320 | break; |
| 321 | case SWT.EraseItem: |
| 322 | entry.erase(event); |
| 323 | break; |
| 324 | } |
| 325 | } |
| 326 | } |
| 327 | }; |
| 328 | table.addListener(SWT.MeasureItem, listener); |
| 329 | table.addListener(SWT.EraseItem, listener); |
| 330 | table.addListener(SWT.PaintItem, listener); |
| 331 | |
| 332 | return composite; |
| 333 | } |
| 334 | |
| 335 | /** |
| 336 | * |
| 337 | */ |
| 338 | private int computeNumberOfItems() { |
| 339 | Rectangle rect = table.getClientArea (); |
| 340 | int itemHeight = table.getItemHeight (); |
| 341 | int headerHeight = table.getHeaderHeight (); |
| 342 | return (rect.height - headerHeight + itemHeight - 1) / (itemHeight + table.getGridLineWidth()); |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * |
| 347 | */ |
| 348 | private void refresh(String filter) { |
| 349 | int numItems = computeNumberOfItems(); |
| 350 | |
| 351 | // perfect match, to be selected in the table if not null |
| 352 | QuickAccessElement perfectMatch = (QuickAccessElement) elementMap |
| 353 | .get(filter); |
| 354 | |
| 355 | List[] entries = computeMatchingEntries(filter, perfectMatch, numItems); |
| 356 | |
| 357 | int selectionIndex = refreshTable(perfectMatch, entries); |
| 358 | |
| 359 | if (table.getItemCount() > 0) { |
| 360 | table.setSelection(selectionIndex); |
| 361 | } else if (filter.length() == 0) { |
| 362 | { |
| 363 | TableItem item = new TableItem(table, SWT.NONE); |
| 364 | item.setText(0, |
| 365 | QuickAccessMessages.QuickAccess_AvailableCategories); |
| 366 | item.setForeground(0, grayColor); |
| 367 | } |
| 368 | for (int i = 0; i < providers.length; i++) { |
| 369 | QuickAccessProvider provider = providers[i]; |
| 370 | TableItem item = new TableItem(table, SWT.NONE); |
| 371 | item.setText(1, provider.getName()); |
| 372 | item.setForeground(1, grayColor); |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | if (filter.length() == 0) { |
| 377 | setInfoText(QuickAccessMessages.QuickAccess_StartTypingToFindMatches); |
| 378 | } else { |
| 379 | TriggerSequence[] sequences = getInvokingCommandKeySequences(); |
| 380 | if (showAllMatches || sequences == null || sequences.length == 0) { |
| 381 | setInfoText(""); //$NON-NLS-1$ |
| 382 | } else { |
| 383 | setInfoText(NLS |
| 384 | .bind( |
| 385 | QuickAccessMessages.QuickAccess_PressKeyToShowAllMatches, |
| 386 | sequences[0].format())); |
| 387 | } |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | final protected TriggerSequence[] getInvokingCommandKeySequences() { |
| 392 | if (invokingCommandKeySequences == null) { |
| 393 | if (invokingCommand != null) { |
| 394 | IBindingService bindingService = (IBindingService) window |
| 395 | .getWorkbench().getAdapter(IBindingService.class); |
| 396 | invokingCommandKeySequences = bindingService |
| 397 | .getActiveBindingsFor(invokingCommand.getId()); |
| 398 | } |
| 399 | } |
| 400 | return invokingCommandKeySequences; |
| 401 | } |
| 402 | |
| 403 | private KeyAdapter getKeyAdapter() { |
| 404 | if (keyAdapter == null) { |
| 405 | keyAdapter = new KeyAdapter() { |
| 406 | public void keyPressed(KeyEvent e) { |
| 407 | int accelerator = SWTKeySupport |
| 408 | .convertEventToUnmodifiedAccelerator(e); |
| 409 | KeySequence keySequence = KeySequence |
| 410 | .getInstance(SWTKeySupport |
| 411 | .convertAcceleratorToKeyStroke(accelerator)); |
| 412 | TriggerSequence[] sequences = getInvokingCommandKeySequences(); |
| 413 | if (sequences == null) |
| 414 | return; |
| 415 | for (int i = 0; i < sequences.length; i++) { |
| 416 | if (sequences[i].equals(keySequence)) { |
| 417 | e.doit = false; |
| 418 | toggleShowAllMatches(); |
| 419 | return; |
| 420 | } |
| 421 | } |
| 422 | } |
| 423 | }; |
| 424 | } |
| 425 | return keyAdapter; |
| 426 | } |
| 427 | |
| 428 | private void toggleShowAllMatches() { |
| 429 | showAllMatches = !showAllMatches; |
| 430 | refresh(filterText.getText().toLowerCase()); |
| 431 | } |
| 432 | |
| 433 | private int refreshTable(QuickAccessElement perfectMatch, List[] entries) { |
| 434 | if (table.getItemCount() > entries.length |
| 435 | && table.getItemCount() - entries.length > 20) { |
| 436 | table.removeAll(); |
| 437 | } |
| 438 | TableItem[] items = table.getItems(); |
| 439 | int selectionIndex = -1; |
| 440 | int index = 0; |
| 441 | for (int i = 0; i < providers.length; i++) { |
| 442 | if (entries[i] != null) { |
| 443 | boolean firstEntry = true; |
| 444 | for (Iterator it = entries[i].iterator(); it.hasNext();) { |
| 445 | QuickAccessEntry entry = (QuickAccessEntry) it.next(); |
| 446 | entry.firstInCategory = firstEntry; |
| 447 | firstEntry = false; |
| 448 | if (!it.hasNext()) { |
| 449 | entry.lastInCategory = true; |
| 450 | } |
| 451 | TableItem item; |
| 452 | if (index < items.length) { |
| 453 | item = items[index]; |
| 454 | table.clear(index); |
| 455 | } else { |
| 456 | item = new TableItem(table, SWT.NONE); |
| 457 | } |
| 458 | if (perfectMatch == entry.element && selectionIndex == -1) { |
| 459 | selectionIndex = index; |
| 460 | } |
| 461 | item.setData(entry); |
| 462 | item.setText(0, entry.provider.getName()); |
| 463 | item.setText(1, entry.element.getLabel()); |
| 464 | if (SWT.getPlatform().equals("wpf")) { //$NON-NLS-1$ |
| 465 | item.setImage(1, entry.getImage(entry.element, |
| 466 | resourceManager)); |
| 467 | } |
| 468 | index++; |
| 469 | } |
| 470 | } |
| 471 | } |
| 472 | if (index < items.length) { |
| 473 | table.remove(index, items.length - 1); |
| 474 | } |
| 475 | if (selectionIndex == -1) { |
| 476 | selectionIndex = 0; |
| 477 | } |
| 478 | return selectionIndex; |
| 479 | } |
| 480 | |
| 481 | private List[] computeMatchingEntries(String filter, |
| 482 | QuickAccessElement perfectMatch, int maxCount) { |
| 483 | // collect matches in an array of lists |
| 484 | List[] entries = new ArrayList[providers.length]; |
| 485 | int[] indexPerProvider = new int[providers.length]; |
| 486 | int countPerProvider = Math.min(maxCount / 4, |
| 487 | INITIAL_COUNT_PER_PROVIDER); |
| 488 | int countTotal = 0; |
| 489 | boolean perfectMatchAdded = true; |
| 490 | if (perfectMatch != null) { |
| 491 | // reserve one entry for the perfect match |
| 492 | maxCount--; |
| 493 | perfectMatchAdded = false; |
| 494 | } |
| 495 | boolean done; |
| 496 | do { |
| 497 | // will be set to false if we find a provider with remaining |
| 498 | // elements |
| 499 | done = true; |
| 500 | for (int i = 0; i < providers.length |
| 501 | && (showAllMatches || countTotal < maxCount); i++) { |
| 502 | if (entries[i] == null) { |
| 503 | entries[i] = new ArrayList(); |
| 504 | indexPerProvider[i] = 0; |
| 505 | } |
| 506 | int count = 0; |
| 507 | QuickAccessProvider provider = providers[i]; |
| 508 | if (filter.length() > 0 |
| 509 | || provider instanceof PreviousPicksProvider |
| 510 | || showAllMatches) { |
| 511 | QuickAccessElement[] elements = provider |
| 512 | .getElementsSorted(); |
| 513 | int j = indexPerProvider[i]; |
| 514 | while (j < elements.length |
| 515 | && (showAllMatches || (count < countPerProvider && countTotal < maxCount))) { |
| 516 | QuickAccessElement element = elements[j]; |
| 517 | QuickAccessEntry entry; |
| 518 | if (filter.length() == 0) { |
| 519 | if (i == 0 || showAllMatches) { |
| 520 | entry = new QuickAccessEntry(element, provider, |
| 521 | new int[0][0], new int[0][0]); |
| 522 | } else { |
| 523 | entry = null; |
| 524 | } |
| 525 | } else { |
| 526 | entry = element.match(filter, provider); |
| 527 | } |
| 528 | if (entry != null) { |
| 529 | entries[i].add(entry); |
| 530 | count++; |
| 531 | countTotal++; |
| 532 | if (i == 0 && entry.element == perfectMatch) { |
| 533 | perfectMatchAdded = true; |
| 534 | maxCount = MAX_COUNT_TOTAL; |
| 535 | } |
| 536 | } |
| 537 | j++; |
| 538 | } |
| 539 | indexPerProvider[i] = j; |
| 540 | if (j < elements.length) { |
| 541 | done = false; |
| 542 | } |
| 543 | } |
| 544 | } |
| 545 | // from now on, add one element per provider |
| 546 | countPerProvider = 1; |
| 547 | } while ((showAllMatches || countTotal < maxCount) && !done); |
| 548 | if (!perfectMatchAdded) { |
| 549 | QuickAccessEntry entry = perfectMatch.match(filter, providers[0]); |
| 550 | if (entry != null) { |
| 551 | if (entries[0] == null) { |
| 552 | entries[0] = new ArrayList(); |
| 553 | indexPerProvider[0] = 0; |
| 554 | } |
| 555 | entries[0].add(entry); |
| 556 | } |
| 557 | } |
| 558 | return entries; |
| 559 | } |
| 560 | |
| 561 | protected Control getFocusControl() { |
| 562 | return filterText; |
| 563 | } |
| 564 | |
| 565 | public boolean close() { |
| 566 | storeDialog(getDialogSettings()); |
| 567 | if (textLayout != null && !textLayout.isDisposed()) { |
| 568 | textLayout.dispose(); |
| 569 | } |
| 570 | if (resourceManager != null) { |
| 571 | resourceManager.dispose(); |
| 572 | resourceManager = null; |
| 573 | } |
| 574 | return super.close(); |
| 575 | } |
| 576 | |
| 577 | protected Point getDefaultSize() { |
| 578 | return new Point(350, 420); |
| 579 | } |
| 580 | |
| 581 | protected Point getDefaultLocation(Point initialSize) { |
| 582 | Point size = new Point(400, 400); |
| 583 | Rectangle parentBounds = getParentShell().getBounds(); |
| 584 | int x = parentBounds.x + parentBounds.width / 2 - size.x / 2; |
| 585 | int y = parentBounds.y + parentBounds.height / 2 - size.y / 2; |
| 586 | return new Point(x, y); |
| 587 | } |
| 588 | |
| 589 | protected IDialogSettings getDialogSettings() { |
| 590 | final IDialogSettings workbenchDialogSettings = WorkbenchPlugin |
| 591 | .getDefault().getDialogSettings(); |
| 592 | IDialogSettings result = workbenchDialogSettings.getSection(getId()); |
| 593 | if (result == null) { |
| 594 | result = workbenchDialogSettings.addNewSection(getId()); |
| 595 | } |
| 596 | return result; |
| 597 | } |
| 598 | |
| 599 | protected String getId() { |
| 600 | return "org.eclipse.ui.internal.QuickAccess"; //$NON-NLS-1$ |
| 601 | } |
| 602 | |
| 603 | private void storeDialog(IDialogSettings dialogSettings) { |
| 604 | String[] orderedElements = new String[previousPicksList.size()]; |
| 605 | String[] orderedProviders = new String[previousPicksList.size()]; |
| 606 | String[] textEntries = new String[previousPicksList.size()]; |
| 607 | ArrayList arrayList = new ArrayList(); |
| 608 | for (int i = 0; i < orderedElements.length; i++) { |
| 609 | QuickAccessElement quickAccessElement = (QuickAccessElement) previousPicksList |
| 610 | .get(i); |
| 611 | ArrayList elementText = (ArrayList) textMap.get(quickAccessElement); |
| 612 | Assert.isNotNull(elementText); |
| 613 | orderedElements[i] = quickAccessElement.getId(); |
| 614 | orderedProviders[i] = quickAccessElement.getProvider().getId(); |
| 615 | arrayList.addAll(elementText); |
| 616 | textEntries[i] = elementText.size() + ""; //$NON-NLS-1$ |
| 617 | } |
| 618 | String[] textArray = (String[]) arrayList.toArray(new String[arrayList |
| 619 | .size()]); |
| 620 | dialogSettings.put(ORDERED_ELEMENTS, orderedElements); |
| 621 | dialogSettings.put(ORDERED_PROVIDERS, orderedProviders); |
| 622 | dialogSettings.put(TEXT_ENTRIES, textEntries); |
| 623 | dialogSettings.put(TEXT_ARRAY, textArray); |
| 624 | } |
| 625 | |
| 626 | private void restoreDialog() { |
| 627 | IDialogSettings dialogSettings = getDialogSettings(); |
| 628 | if (dialogSettings != null) { |
| 629 | String[] orderedElements = dialogSettings |
| 630 | .getArray(ORDERED_ELEMENTS); |
| 631 | String[] orderedProviders = dialogSettings |
| 632 | .getArray(ORDERED_PROVIDERS); |
| 633 | String[] textEntries = dialogSettings.getArray(TEXT_ENTRIES); |
| 634 | String[] textArray = dialogSettings.getArray(TEXT_ARRAY); |
| 635 | elementMap = new HashMap(); |
| 636 | textMap = new HashMap(); |
| 637 | previousPicksList = new LinkedList(); |
| 638 | if (orderedElements != null && orderedProviders != null |
| 639 | && textEntries != null && textArray != null) { |
| 640 | int arrayIndex = 0; |
| 641 | for (int i = 0; i < orderedElements.length; i++) { |
| 642 | QuickAccessProvider quickAccessProvider = (QuickAccessProvider) providerMap |
| 643 | .get(orderedProviders[i]); |
| 644 | int numTexts = Integer.parseInt(textEntries[i]); |
| 645 | if (quickAccessProvider != null) { |
| 646 | QuickAccessElement quickAccessElement = quickAccessProvider |
| 647 | .getElementForId(orderedElements[i]); |
| 648 | if (quickAccessElement != null) { |
| 649 | ArrayList arrayList = new ArrayList(); |
| 650 | for (int j = arrayIndex; j < arrayIndex + numTexts; j++) { |
| 651 | String text = textArray[j]; |
| 652 | // text length can be zero for old workspaces, see bug 190006 |
| 653 | if (text.length() > 0) { |
| 654 | arrayList.add(text); |
| 655 | elementMap.put(text, quickAccessElement); |
| 656 | } |
| 657 | } |
| 658 | textMap.put(quickAccessElement, arrayList); |
| 659 | previousPicksList.add(quickAccessElement); |
| 660 | } |
| 661 | } |
| 662 | arrayIndex += numTexts; |
| 663 | } |
| 664 | } |
| 665 | } |
| 666 | } |
| 667 | |
| 668 | protected void handleElementSelected(String text, Object selectedElement) { |
| 669 | IWorkbenchPage activePage = window.getActivePage(); |
| 670 | if (activePage != null) { |
| 671 | if (selectedElement instanceof QuickAccessElement) { |
| 672 | addPreviousPick(text, selectedElement); |
| 673 | storeDialog(getDialogSettings()); |
| 674 | QuickAccessElement element = (QuickAccessElement) selectedElement; |
| 675 | element.execute(); |
| 676 | } |
| 677 | } |
| 678 | } |
| 679 | |
| 680 | /** |
| 681 | * @param element |
| 682 | */ |
| 683 | private void addPreviousPick(String text, Object element) { |
| 684 | // previousPicksList: |
| 685 | // Remove element from previousPicksList so there are no duplicates |
| 686 | // If list is max size, remove last(oldest) element |
| 687 | // Remove entries for removed element from elementMap and textMap |
| 688 | // Add element to front of previousPicksList |
| 689 | previousPicksList.remove(element); |
| 690 | if (previousPicksList.size() == MAXIMUM_NUMBER_OF_ELEMENTS) { |
| 691 | Object removedElement = previousPicksList.removeLast(); |
| 692 | ArrayList removedList = (ArrayList) textMap.remove(removedElement); |
| 693 | for (int i = 0; i < removedList.size(); i++) { |
| 694 | elementMap.remove(removedList.get(i)); |
| 695 | } |
| 696 | } |
| 697 | previousPicksList.addFirst(element); |
| 698 | |
| 699 | // textMap: |
| 700 | // Get list of strings for element from textMap |
| 701 | // Create new list for element if there isn't one and put |
| 702 | // element->textList in textMap |
| 703 | // Remove rememberedText from list |
| 704 | // If list is max size, remove first(oldest) string |
| 705 | // Remove text from elementMap |
| 706 | // Add rememberedText to list of strings for element in textMap |
| 707 | ArrayList textList = (ArrayList) textMap.get(element); |
| 708 | if (textList == null) { |
| 709 | textList = new ArrayList(); |
| 710 | textMap.put(element, textList); |
| 711 | } |
| 712 | |
| 713 | textList.remove(text); |
| 714 | if (textList.size() == MAXIMUM_NUMBER_OF_TEXT_ENTRIES_PER_ELEMENT) { |
| 715 | Object removedText = textList.remove(0); |
| 716 | elementMap.remove(removedText); |
| 717 | } |
| 718 | |
| 719 | if (text.length() > 0) { |
| 720 | textList.add(text); |
| 721 | |
| 722 | // elementMap: |
| 723 | // Put rememberedText->element in elementMap |
| 724 | // If it replaced a different element update textMap and |
| 725 | // PreviousPicksList |
| 726 | Object replacedElement = elementMap.put(text, element); |
| 727 | if (replacedElement != null && !replacedElement.equals(element)) { |
| 728 | textList = (ArrayList) textMap.get(replacedElement); |
| 729 | if (textList != null) { |
| 730 | textList.remove(text); |
| 731 | if (textList.isEmpty()) { |
| 732 | textMap.remove(replacedElement); |
| 733 | previousPicksList.remove(replacedElement); |
| 734 | } |
| 735 | } |
| 736 | } |
| 737 | } |
| 738 | } |
| 739 | |
| 740 | /** |
| 741 | * |
| 742 | */ |
| 743 | private void handleSelection() { |
| 744 | QuickAccessElement selectedElement = null; |
| 745 | String text = filterText.getText().toLowerCase(); |
| 746 | if (table.getSelectionCount() == 1) { |
| 747 | QuickAccessEntry entry = (QuickAccessEntry) table |
| 748 | .getSelection()[0].getData(); |
| 749 | selectedElement = entry == null ? null : entry.element; |
| 750 | } |
| 751 | close(); |
| 752 | if (selectedElement != null) { |
| 753 | handleElementSelected(text, selectedElement); |
| 754 | } |
| 755 | } |
| 756 | |
| 757 | private class PreviousPicksProvider extends QuickAccessProvider { |
| 758 | |
| 759 | public QuickAccessElement getElementForId(String id) { |
| 760 | return null; |
| 761 | } |
| 762 | |
| 763 | public QuickAccessElement[] getElements() { |
| 764 | return (QuickAccessElement[]) previousPicksList |
| 765 | .toArray(new QuickAccessElement[previousPicksList.size()]); |
| 766 | } |
| 767 | |
| 768 | public QuickAccessElement[] getElementsSorted() { |
| 769 | return getElements(); |
| 770 | } |
| 771 | |
| 772 | public String getId() { |
| 773 | return "org.eclipse.ui.previousPicks"; //$NON-NLS-1$ |
| 774 | } |
| 775 | |
| 776 | public ImageDescriptor getImageDescriptor() { |
| 777 | return WorkbenchImages |
| 778 | .getImageDescriptor(IWorkbenchGraphicConstants.IMG_OBJ_NODE); |
| 779 | } |
| 780 | |
| 781 | public String getName() { |
| 782 | return QuickAccessMessages.QuickAccess_Previous; |
| 783 | } |
| 784 | } |
| 785 | |
| 786 | } |