KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > pde > internal > ui > editor > outline > QuickOutlinePopupDialog


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 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
12 package org.eclipse.pde.internal.ui.editor.outline;
13
14 import org.eclipse.jface.action.IMenuManager;
15 import org.eclipse.jface.action.Separator;
16 import org.eclipse.jface.dialogs.Dialog;
17 import org.eclipse.jface.dialogs.PopupDialog;
18 import org.eclipse.jface.text.IInformationControl;
19 import org.eclipse.jface.text.IInformationControlExtension;
20 import org.eclipse.jface.text.IInformationControlExtension2;
21 import org.eclipse.jface.viewers.AbstractTreeViewer;
22 import org.eclipse.jface.viewers.ILabelProvider;
23 import org.eclipse.jface.viewers.IStructuredSelection;
24 import org.eclipse.jface.viewers.ITreeContentProvider;
25 import org.eclipse.jface.viewers.StructuredSelection;
26 import org.eclipse.jface.viewers.TreeViewer;
27 import org.eclipse.jface.viewers.ViewerComparator;
28 import org.eclipse.pde.internal.ui.PDEUIMessages;
29 import org.eclipse.pde.internal.ui.editor.actions.SortAction;
30 import org.eclipse.pde.internal.ui.util.StringMatcher;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.events.DisposeEvent;
33 import org.eclipse.swt.events.DisposeListener;
34 import org.eclipse.swt.events.FocusListener;
35 import org.eclipse.swt.events.KeyEvent;
36 import org.eclipse.swt.events.KeyListener;
37 import org.eclipse.swt.events.ModifyEvent;
38 import org.eclipse.swt.events.ModifyListener;
39 import org.eclipse.swt.events.MouseAdapter;
40 import org.eclipse.swt.events.MouseEvent;
41 import org.eclipse.swt.events.SelectionEvent;
42 import org.eclipse.swt.events.SelectionListener;
43 import org.eclipse.swt.graphics.Color;
44 import org.eclipse.swt.graphics.FontMetrics;
45 import org.eclipse.swt.graphics.GC;
46 import org.eclipse.swt.graphics.Point;
47 import org.eclipse.swt.layout.GridData;
48 import org.eclipse.swt.widgets.Composite;
49 import org.eclipse.swt.widgets.Control;
50 import org.eclipse.swt.widgets.Shell;
51 import org.eclipse.swt.widgets.Text;
52 import org.eclipse.swt.widgets.Tree;
53 import org.eclipse.swt.widgets.TreeItem;
54
55 /**
56  * AbstractInfoPopupDialog
57  *
58  */

59 public class QuickOutlinePopupDialog extends PopupDialog implements
60         IInformationControl, IInformationControlExtension,
61         IInformationControlExtension2, DisposeListener {
62
63     private TreeViewer fTreeViewer;
64     
65     private IOutlineContentCreator fOutlineContentCreator;
66     
67     private IOutlineSelectionHandler fOutlineSelectionHandler;
68     
69     private Text fFilterText;
70     
71     private StringMatcher fStringMatcher;
72     
73     private QuickOutlineNamePatternFilter fNamePatternFilter;
74     
75     private SortAction fSortAction;
76     
77     private ITreeContentProvider fTreeContentProvider;
78     
79     private ILabelProvider fTreeLabelProvider;
80     
81     private ViewerComparator fTreeViewerComparator;
82
83     private ViewerComparator fTreeViewerDefaultComparator;
84     
85     public QuickOutlinePopupDialog(Shell parent, int shellStyle,
86             IOutlineContentCreator creator,
87             IOutlineSelectionHandler handler) {
88         super(parent, shellStyle, true, true, true, true, null, null);
89         // Set outline creator
90
fOutlineContentCreator = creator;
91         // Set outline handler
92
fOutlineSelectionHandler = handler;
93         // Initialize the other fields
94
initialize();
95         // Create all controls early to preserve the life cycle of the original
96
// implementation.
97
create();
98     }
99     
100     /**
101      *
102      */

103     private void initialize() {
104         setInfoText(PDEUIMessages.QuickOutlinePopupDialog_infoTextPressEscToExit);
105         
106         fFilterText = null;
107         fTreeViewer = null;
108         fStringMatcher = null;
109         fNamePatternFilter = null;
110         fSortAction = null;
111         fTreeContentProvider = null;
112         fTreeLabelProvider = null;
113         fTreeViewerComparator = null;
114         fTreeViewerDefaultComparator = null;
115     }
116
117     /* (non-Javadoc)
118      * @see org.eclipse.jface.dialogs.PopupDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
119      */

120     protected Control createDialogArea(Composite parent) {
121         // Applies only to dialog body - not title. See createTitleControl
122
// Create an empty dialog area, if the source page is not defined
123
if ((fOutlineContentCreator == null) ||
124                 (fOutlineSelectionHandler == null)) {
125             return super.createDialogArea(parent);
126         }
127         // Create the tree viewer
128
createUIWidgetTreeViewer(parent);
129         // Add listeners to the tree viewer
130
createUIListenersTreeViewer();
131         // Create the actions
132
createUIActions();
133         // Add a dispose listner
134
addDisposeListener(this);
135         // Return the tree
136
return fTreeViewer.getControl();
137     }
138
139     /**
140      *
141      */

142     private void createUIActions() {
143         // Add sort action to dialog menu
144
fSortAction = new SortAction(fTreeViewer,
145                 PDEUIMessages.PDEMultiPageContentOutline_SortingAction_tooltip,
146                 fTreeViewerComparator, fTreeViewerDefaultComparator, null);
147     }
148
149     /* (non-Javadoc)
150      * @see org.eclipse.jface.dialogs.PopupDialog#fillDialogMenu(org.eclipse.jface.action.IMenuManager)
151      */

152     protected void fillDialogMenu(IMenuManager dialogMenu) {
153         // Add the sort action
154
dialogMenu.add(fSortAction);
155         // Separator
156
dialogMenu.add(new Separator());
157         // Add the default actions
158
super.fillDialogMenu(dialogMenu);
159     }
160     
161     /**
162      * @param parent
163      */

164     private void createUIWidgetTreeViewer(Composite parent) {
165         
166         // NOTE: Instructions to implement for PDE form pages:
167
// Need to call PDEFormEditor.getFormOutline()
168
// Specify PDE form editor as input
169
// Need to adjust commandId="org.eclipse.pde.ui.quickOutline"
170
// scope: contextId="org.eclipse.ui.textEditorScope"
171
// SEE org.eclipse.ui.contexts.window
172
// TODO: MP: QO: LOW: Implement bi-directional support between form and source page for manifest
173

174         int style = SWT.H_SCROLL | SWT.V_SCROLL;
175         // Create the tree
176
Tree widget = new Tree(parent, style);
177         // Configure the layout
178
GridData data = new GridData(GridData.FILL_BOTH);
179         data.heightHint = widget.getItemHeight() * 12;
180         widget.setLayoutData(data);
181         // Create the tree viewer
182
fTreeViewer = new TreeViewer(widget);
183         // Add the name pattern filter
184
fNamePatternFilter = new QuickOutlineNamePatternFilter();
185         fTreeViewer.addFilter(fNamePatternFilter);
186         // Set the content provider
187
fTreeContentProvider = fOutlineContentCreator.createOutlineContentProvider();
188         fTreeViewer.setContentProvider(fTreeContentProvider);
189         // Set the label provider
190
fTreeLabelProvider = fOutlineContentCreator.createOutlineLabelProvider();
191         fTreeViewer.setLabelProvider(fTreeLabelProvider);
192         // Create the outline sorter (to be set on the sort action)
193
fTreeViewerComparator = fOutlineContentCreator.createOutlineComparator();
194         // Set the comparator to null (sort action will be disabled initially
195
// because of this)
196
// Create the default outline sorter (Most like this will just return
197
// null to indicate sorting disabled
198
fTreeViewerDefaultComparator =
199             fOutlineContentCreator.createDefaultOutlineComparator();
200         fTreeViewer.setComparator(fTreeViewerDefaultComparator);
201         fTreeViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
202         fTreeViewer.setUseHashlookup(true);
203         fTreeViewer.setInput(fOutlineContentCreator.getOutlineInput());
204     }
205     
206     /**
207      *
208      */

209     private void createUIListenersTreeViewer() {
210         // Get the underlying tree widget
211
final Tree tree = fTreeViewer.getTree();
212         // Handle key events
213
tree.addKeyListener(new KeyListener() {
214             public void keyPressed(KeyEvent e) {
215                 if (e.character == 0x1B) {
216                     // Dispose on ESC key press
217
dispose();
218                 }
219             }
220             public void keyReleased(KeyEvent e) {
221                 // NO-OP
222
}
223         });
224         // Handle mouse clicks
225
tree.addMouseListener(new MouseAdapter() {
226             public void mouseUp(MouseEvent e) {
227                 handleTreeViewerMouseUp(tree, e);
228             }
229         });
230         // Handle mouse move events
231
tree.addMouseMoveListener(new QuickOutlineMouseMoveListener(fTreeViewer));
232         // Handle widget selection events
233
tree.addSelectionListener(new SelectionListener() {
234             public void widgetSelected(SelectionEvent e) {
235                 // NO-OP
236
}
237             public void widgetDefaultSelected(SelectionEvent e) {
238                 gotoSelectedElement();
239             }
240         });
241     }
242
243     /**
244      * @param tree
245      * @param e
246      */

247     private void handleTreeViewerMouseUp(final Tree tree, MouseEvent e) {
248         // Ensure a selection was made, the first mouse button was
249
// used and the event happened in the tree
250
if ((tree.getSelectionCount() < 1) ||
251                 (e.button != 1) ||
252                 (tree.equals(e.getSource()) == false)) {
253             return;
254         }
255         // Selection is made in the selection changed listener
256
Object JavaDoc object = tree.getItem(new Point(e.x, e.y));
257         TreeItem selection = tree.getSelection()[0];
258         if (selection.equals(object)) {
259             gotoSelectedElement();
260         }
261     }
262     
263     /**
264      * @return
265      */

266     private Object JavaDoc getSelectedElement() {
267         if (fTreeViewer == null) {
268             return null;
269         }
270         return ((IStructuredSelection) fTreeViewer.getSelection()).getFirstElement();
271     }
272
273     /* (non-Javadoc)
274      * @see org.eclipse.jface.text.IInformationControl#addDisposeListener(org.eclipse.swt.events.DisposeListener)
275      */

276     public void addDisposeListener(DisposeListener listener) {
277         getShell().addDisposeListener(listener);
278     }
279
280     /* (non-Javadoc)
281      * @see org.eclipse.jface.text.IInformationControl#addFocusListener(org.eclipse.swt.events.FocusListener)
282      */

283     public void addFocusListener(FocusListener listener) {
284         getShell().addFocusListener(listener);
285     }
286
287     /* (non-Javadoc)
288      * @see org.eclipse.jface.text.IInformationControl#computeSizeHint()
289      */

290     public Point computeSizeHint() {
291         // Return the shell's size
292
// Note that it already has the persisted size if persisting is enabled.
293
return getShell().getSize();
294     }
295
296     /* (non-Javadoc)
297      * @see org.eclipse.jface.text.IInformationControl#dispose()
298      */

299     public void dispose() {
300         close();
301     }
302
303     /* (non-Javadoc)
304      * @see org.eclipse.jface.text.IInformationControl#isFocusControl()
305      */

306     public boolean isFocusControl() {
307         if (fTreeViewer.getControl().isFocusControl() ||
308                 fFilterText.isFocusControl()) {
309             return true;
310         }
311         return false;
312     }
313
314     /* (non-Javadoc)
315      * @see org.eclipse.jface.text.IInformationControl#removeDisposeListener(org.eclipse.swt.events.DisposeListener)
316      */

317     public void removeDisposeListener(DisposeListener listener) {
318         getShell().removeDisposeListener(listener);
319     }
320
321     /* (non-Javadoc)
322      * @see org.eclipse.jface.text.IInformationControl#removeFocusListener(org.eclipse.swt.events.FocusListener)
323      */

324     public void removeFocusListener(FocusListener listener) {
325         getShell().removeFocusListener(listener);
326     }
327
328     /* (non-Javadoc)
329      * @see org.eclipse.jface.text.IInformationControl#setBackgroundColor(org.eclipse.swt.graphics.Color)
330      */

331     public void setBackgroundColor(Color background) {
332         applyBackgroundColor(background, getContents());
333     }
334
335     /* (non-Javadoc)
336      * @see org.eclipse.jface.text.IInformationControl#setFocus()
337      */

338     public void setFocus() {
339         getShell().forceFocus();
340         fFilterText.setFocus();
341     }
342
343     /* (non-Javadoc)
344      * @see org.eclipse.jface.text.IInformationControl#setForegroundColor(org.eclipse.swt.graphics.Color)
345      */

346     public void setForegroundColor(Color foreground) {
347         applyForegroundColor(foreground, getContents());
348     }
349
350     /* (non-Javadoc)
351      * @see org.eclipse.jface.text.IInformationControl#setInformation(java.lang.String)
352      */

353     public void setInformation(String JavaDoc information) {
354         // Ignore
355
// See IInformationControlExtension2
356
}
357
358     /* (non-Javadoc)
359      * @see org.eclipse.jface.text.IInformationControl#setLocation(org.eclipse.swt.graphics.Point)
360      */

361     public void setLocation(Point location) {
362         /*
363          * If the location is persisted, it gets managed by PopupDialog - fine. Otherwise, the location is
364          * computed in Window#getInitialLocation, which will center it in the parent shell / main
365          * monitor, which is wrong for two reasons:
366          * - we want to center over the editor / subject control, not the parent shell
367          * - the center is computed via the initalSize, which may be also wrong since the size may
368          * have been updated since via min/max sizing of AbstractInformationControlManager.
369          * In that case, override the location with the one computed by the manager. Note that
370          * the call to constrainShellSize in PopupDialog.open will still ensure that the shell is
371          * entirely visible.
372          */

373         if ((getPersistBounds() == false) ||
374                 (getDialogSettings() == null)) {
375             getShell().setLocation(location);
376         }
377     }
378
379     /* (non-Javadoc)
380      * @see org.eclipse.jface.text.IInformationControl#setSize(int, int)
381      */

382     public void setSize(int width, int height) {
383         getShell().setSize(width, height);
384     }
385
386     /* (non-Javadoc)
387      * @see org.eclipse.jface.text.IInformationControl#setSizeConstraints(int, int)
388      */

389     public void setSizeConstraints(int maxWidth, int maxHeight) {
390         // Ignore
391
}
392
393     /* (non-Javadoc)
394      * @see org.eclipse.jface.text.IInformationControl#setVisible(boolean)
395      */

396     public void setVisible(boolean visible) {
397         if (visible) {
398             open();
399         } else {
400             saveDialogBounds(getShell());
401             getShell().setVisible(false);
402         }
403     }
404
405     /* (non-Javadoc)
406      * @see org.eclipse.jface.text.IInformationControlExtension#hasContents()
407      */

408     public boolean hasContents() {
409         if ((fTreeViewer == null) ||
410                 (fTreeViewer.getInput() == null)) {
411             return false;
412         }
413         return true;
414     }
415
416     /* (non-Javadoc)
417      * @see org.eclipse.jface.text.IInformationControlExtension2#setInput(java.lang.Object)
418      */

419     public void setInput(Object JavaDoc input) {
420         // Input comes from PDESourceInfoProvider.getInformation2()
421
// The input should be a model object of some sort
422
// Turn it into a structured selection and set the selection in the tree
423
if (input != null) {
424             fTreeViewer.setSelection(new StructuredSelection(input));
425         }
426     }
427
428     /* (non-Javadoc)
429      * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
430      */

431     public void widgetDisposed(DisposeEvent e) {
432         // Note: We do not reuse the dialog
433
fTreeViewer= null;
434         fFilterText= null;
435     }
436
437     /* (non-Javadoc)
438      * @see org.eclipse.jface.dialogs.PopupDialog#createTitleControl(org.eclipse.swt.widgets.Composite)
439      */

440     protected Control createTitleControl(Composite parent) {
441         // Applies only to dialog title - not body. See createDialogArea
442
// Create the text widget
443
createUIWidgetFilterText(parent);
444         // Add listeners to the text widget
445
createUIListenersFilterText();
446         // Return the text widget
447
return fFilterText;
448     }
449     
450     /**
451      * @param parent
452      * @return
453      */

454     private void createUIWidgetFilterText(Composite parent) {
455         // Create the widget
456
fFilterText = new Text(parent, SWT.NONE);
457         // Set the font
458
GC gc = new GC(parent);
459         gc.setFont(parent.getFont());
460         FontMetrics fontMetrics = gc.getFontMetrics();
461         gc.dispose();
462         // Create the layout
463
GridData data = new GridData(GridData.FILL_HORIZONTAL);
464         data.heightHint= Dialog.convertHeightInCharsToPixels(fontMetrics, 1);
465         data.horizontalAlignment = GridData.FILL;
466         data.verticalAlignment = GridData.CENTER;
467         fFilterText.setLayoutData(data);
468     }
469
470     /**
471      *
472      */

473     private void gotoSelectedElement() {
474         Object JavaDoc selectedElement = getSelectedElement();
475         if (selectedElement == null) {
476             return;
477         }
478         dispose();
479         // Get the content outline page within the content outline view
480
// and select the item there to keep the quick outline in sync with the
481
// main outline and prevent duplicate selection events from occurring
482
fOutlineSelectionHandler.getContentOutline().setSelection(
483                 new StructuredSelection(selectedElement));
484     }
485     
486     /**
487      *
488      */

489     private void createUIListenersFilterText() {
490         // Handle key events
491
fFilterText.addKeyListener(new KeyListener() {
492             public void keyPressed(KeyEvent e) {
493                 if (e.keyCode == 0x0D) {
494                     // Return key was pressed
495
gotoSelectedElement();
496                 } else if (e.keyCode == SWT.ARROW_DOWN) {
497                     // Down key was pressed
498
fTreeViewer.getTree().setFocus();
499                 } else if (e.keyCode == SWT.ARROW_UP) {
500                     // Up key was pressed
501
fTreeViewer.getTree().setFocus();
502                 } else if (e.character == 0x1B) {
503                     // Escape key was pressed
504
dispose();
505                 }
506             }
507             public void keyReleased(KeyEvent e) {
508                 // NO-OP
509
}
510         });
511         // Handle text modify events
512
fFilterText.addModifyListener(new ModifyListener() {
513             public void modifyText(ModifyEvent e) {
514                 String JavaDoc text = ((Text)e.widget).getText();
515                 int length = text.length();
516                 if (length > 0) {
517                     // Append a '*' pattern to the end of the text value if it
518
// does not have one already
519
if (text.charAt(length - 1) != '*') {
520                         text = text + '*';
521                     }
522                     // Prepend a '*' pattern to the beginning of the text value
523
// if it does not have one already
524
if (text.charAt(0) != '*') {
525                         text = '*' + text;
526                     }
527                 }
528                 // Set and update the pattern
529
setMatcherString(text, true);
530             }
531         });
532     }
533     
534     /**
535      * Sets the patterns to filter out for the receiver.
536      * <p>
537      * The following characters have special meaning:
538      * ? => any character
539      * * => any string
540      * </p>
541      *
542      * @param pattern the pattern
543      * @param update <code>true</code> if the viewer should be updated
544      */

545     private void setMatcherString(String JavaDoc pattern, boolean update) {
546         if (pattern.length() == 0) {
547             fStringMatcher = null;
548         } else {
549             fStringMatcher = new StringMatcher(pattern, true, false);
550         }
551         // Update the name pattern filter on the tree viewer
552
fNamePatternFilter.setStringMatcher(fStringMatcher);
553         // Update the tree viewer according to the pattern
554
if (update) {
555             stringMatcherUpdated();
556         }
557     }
558     
559     /**
560      * The string matcher has been modified. The default implementation
561      * refreshes the view and selects the first matched element
562      */

563     private void stringMatcherUpdated() {
564         // Refresh the tree viewer to re-filter
565
fTreeViewer.getControl().setRedraw(false);
566         fTreeViewer.refresh();
567         fTreeViewer.expandAll();
568         selectFirstMatch();
569         fTreeViewer.getControl().setRedraw(true);
570     }
571     
572     /**
573      * Selects the first element in the tree which
574      * matches the current filter pattern.
575      */

576     private void selectFirstMatch() {
577         Tree tree = fTreeViewer.getTree();
578         Object JavaDoc element = findFirstMatchToPattern(tree.getItems());
579         if (element != null) {
580             fTreeViewer.setSelection(new StructuredSelection(element), true);
581         } else {
582             fTreeViewer.setSelection(StructuredSelection.EMPTY);
583         }
584     }
585
586     /**
587      * @param items
588      * @return
589      */

590     private Object JavaDoc findFirstMatchToPattern(TreeItem[] items) {
591         // Match the string pattern against labels
592
ILabelProvider labelProvider =
593             (ILabelProvider)fTreeViewer.getLabelProvider();
594         // Process each item in the tree
595
for (int i = 0; i < items.length; i++) {
596             Object JavaDoc element = items[i].getData();
597             // Return the first element if no pattern is set
598
if (fStringMatcher == null) {
599                 return element;
600             }
601             // Return the element if it matches the pattern
602
if (element != null) {
603                 String JavaDoc label = labelProvider.getText(element);
604                 if (fStringMatcher.match(label)) {
605                     return element;
606                 }
607             }
608             // Recursively check the elements children for a match
609
element = findFirstMatchToPattern(items[i].getItems());
610             // Return the child element match if found
611
if (element != null) {
612                 return element;
613             }
614         }
615         // No match found
616
return null;
617     }
618     
619 }
620
Popular Tags