Java tutorial
/** * Aptana Studio * Copyright (c) 2012 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). * Please see the license.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.snippets.ui.views; import java.io.File; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.search.internal.core.text.PatternConstructor; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSource; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.DragSourceListener; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.ExpandEvent; import org.eclipse.swt.events.ExpandListener; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.ExpandBar; import org.eclipse.swt.widgets.ExpandItem; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IPartListener; import org.eclipse.ui.IPartService; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.MultiPageEditorPart; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.texteditor.ITextEditor; import com.aptana.core.util.CollectionsUtil; import com.aptana.core.util.EclipseUtil; import com.aptana.core.util.StringUtil; import com.aptana.editor.common.AbstractThemeableEditor; import com.aptana.editor.common.CommonEditorPlugin; import com.aptana.editor.common.dnd.SnippetTransfer; import com.aptana.editor.common.scripting.commands.CommandExecutionUtils; import com.aptana.editor.common.scripting.commands.TextEditorUtils; import com.aptana.scripting.model.BundleManager; import com.aptana.scripting.model.CommandResult; import com.aptana.scripting.model.InvocationType; import com.aptana.scripting.model.LoadCycleListener; import com.aptana.scripting.model.SnippetCategoryElement; import com.aptana.scripting.model.SnippetElement; import com.aptana.scripting.ui.ScriptingUIPlugin; import com.aptana.theme.ColorManager; import com.aptana.theme.IControlThemerFactory; import com.aptana.theme.IThemeManager; import com.aptana.theme.Theme; import com.aptana.theme.ThemePlugin; import com.aptana.theme.preferences.IPreferenceConstants; import com.aptana.ui.util.UIUtils; import com.aptana.ui.widgets.SearchComposite; /** * View displays snippets that match the current active editor's context * * @author nle */ public class SnippetsView extends ViewPart { private static final String ICONS_FULL_ELCL16_EXPANDALL_GIF = "icons/full/elcl16/expandall.gif"; //$NON-NLS-1$ public static final String ID = "com.aptana.snippets.ui.views.SnippetsView"; //$NON-NLS-1$ private Image informationImage = ScriptingUIPlugin.getImage("icons/full/obj16/information.png"); //$NON-NLS-1$$ private Image genericSnippetImage = ScriptingUIPlugin.getImage("icons/snippet.png"); //$NON-NLS-1$ private Image genericDisabledSnippetImage = new Image(genericSnippetImage.getDevice(), genericSnippetImage, SWT.IMAGE_DISABLE); private Image insertSnippetImage = ScriptingUIPlugin.getImage("icons/full/elcl16/insert_snippet_tsk.png"); //$NON-NLS-1$ private Map<String, ExpandItem> expandItems = new HashMap<String, ExpandItem>(); private ScrolledComposite scrolledComposite; private ExpandBar expandBar; private SearchComposite search; private SnippetsFilter filter; private Map<String, SnippetCategoryElement> snippetCategories = new LinkedHashMap<String, SnippetCategoryElement>(); private HashMap<String, List<SnippetData>> sortedSnippets = new LinkedHashMap<String, List<SnippetData>>(); private Map<SnippetData, SnippetItem> snippetItems = new HashMap<SnippetData, SnippetItem>(); private Map<String, List<ToolItem>> toolItemMap = new HashMap<String, List<ToolItem>>(); private Font defaultFont, themeFont; private ImageRegistry imageRegistry, disabledImageRegistry; private ImageRegistry hotTagImageRegistry; private ColorManager colorManager; private Color themeBgColor, themeFgColor, themeSelectionColor; private IControlThemerFactory themeFactory; private IPreferenceChangeListener themeListener; private RGB defaultTagHotFg = new RGB(135, 160, 208); private RGB defaultTagHotBg = new RGB(192, 201, 219); private RGB defaultTagBg = new RGB(222, 231, 249); private RGB defaultTagFg = new RGB(165, 190, 238); private static final int FG_ALPHA = 128; private List<Control> themedControls = new ArrayList<Control>(); private List<String> collapsedCategories = new ArrayList<String>(); private String currentScope; private SnippetPopupDialog snippetDialog; private SnippetBundleListener snippetBundleListener; private int tagWidth = -1; private static Pattern descriptionReplacePattern = Pattern.compile("[\r\n\t]+"); //$NON-NLS-1$ class SnippetBundleListener implements LoadCycleListener { private final Job job = new Job("Force reconcile on bundle change") //$NON-NLS-1$ { @Override protected IStatus run(IProgressMonitor monitor) { Display.getDefault().asyncExec(new Runnable() { public void run() { if (scrolledComposite != null && !scrolledComposite.isDisposed()) { createSnippetDrawers(); updateBasedOnPart(getSite().getPage().getActiveEditor()); } } }); return Status.OK_STATUS; } }; /** * */ public SnippetBundleListener() { BundleManager.getInstance().addLoadCycleListener(this); } public void dispose() { BundleManager.getInstance().removeLoadCycleListener(this); } /* * (non-Javadoc) * @see com.aptana.scripting.model.LoadCycleListener#scriptLoaded(java.io.File) */ public void scriptLoaded(File script) { bundleFileChanged(script); } /* * (non-Javadoc) * @see com.aptana.scripting.model.LoadCycleListener#scriptReloaded(java.io.File) */ public void scriptReloaded(File script) { bundleFileChanged(script); } /* * (non-Javadoc) * @see com.aptana.scripting.model.LoadCycleListener#scriptUnloaded(java.io.File) */ public void scriptUnloaded(File script) { bundleFileChanged(script); } private void bundleFileChanged(File script) { if (script == null || (!script.getName().equals(BundleManager.BUNDLE_FILE) && !(script.getParentFile() != null && BundleManager.SNIPPETS_DIRECTORY_NAME.equals(script.getParentFile().getName())))) { return; } // Run in a job on a delay and cancel/reschedule if it already exists and is scheduled... This should // basically only make us run once if we get hit multiple times in a row. We'll still probably run a few // times, but this should cut it down a lot. job.cancel(); EclipseUtil.setSystemForJob(job); job.schedule(750); } } private class InsertSnippetListener extends MouseAdapter { private SnippetElement snippet; InsertSnippetListener(SnippetElement snippet) { this.snippet = snippet; } public void mouseDoubleClick(MouseEvent e) { insertSnippet(snippet); } } private ISelectionListener selectionListener = new ISelectionListener() { public void selectionChanged(IWorkbenchPart part, ISelection selection) { if (selection instanceof ITextSelection) { updateBasedOnPart(part); } } }; Comparator<SnippetCategoryElement> snippetCategoryElementComparator = new Comparator<SnippetCategoryElement>() { public int compare(SnippetCategoryElement arg0, SnippetCategoryElement arg1) { if ((arg0 == null && arg1 == null) || (arg0 != null && arg0.getDisplayName() == null && arg1 != null && arg1.getDisplayName() == null)) { return 0; } else if (arg0 == null || arg0.getDisplayName() == null) { return 1; } else if (arg1 == null || arg1.getDisplayName() == null) { return -1; } return arg0.getDisplayName().compareTo(arg1.getDisplayName()); } }; /** * UI model for a snippet element. Maintains UI state for a SnippetItem * * @author nle */ private class SnippetData { private SnippetElement snippet; private boolean visuallyEnabled = true; private boolean filtered = false; SnippetData(SnippetElement snippet) { this.snippet = snippet; } } /** * UI representation of a snippet in the Snippets view * * @author nle */ private class SnippetItem extends Composite { private SnippetData snippetData; private MouseListener mouseListener; private Label imageLabel; private Composite textComposite; private CLabel titleLabel; private CLabel descLabel; private ToolBar toolbar; private ToolBar tagToolBar; private String categoryName; // Default margin for the snippet item final int SNIPPET_MARGIN = 5; SnippetItem(Composite parent, SnippetData snippetData) { super(parent, SWT.NONE); this.snippetData = snippetData; mouseListener = new InsertSnippetListener(snippetData.snippet); categoryName = getSnippetCategoryName(snippetData.snippet); setLayout(GridLayoutFactory.fillDefaults() .extendedMargins(SNIPPET_MARGIN, SNIPPET_MARGIN, 1, SNIPPET_MARGIN).spacing(2, 0).numColumns(3) .create()); GridData gridData = GridDataFactory.fillDefaults().grab(true, false).create(); gridData.exclude = snippetData.filtered; setLayoutData(gridData); addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { Color foreground = e.gc.getForeground(); e.gc.setForeground(colorManager .getColor(Theme.alphaBlend(getBackground().getRGB(), getForeground().getRGB(), 100))); Rectangle clientArea = getClientArea(); e.gc.drawLine(clientArea.x - SNIPPET_MARGIN, clientArea.y + clientArea.height - 2, clientArea.x + clientArea.width + SNIPPET_MARGIN, clientArea.y + clientArea.height - 2); e.gc.drawLine(clientArea.x, clientArea.y, clientArea.x, clientArea.y + clientArea.height - 1); e.gc.drawLine(clientArea.x + clientArea.width - 1, clientArea.y, clientArea.x + clientArea.width - 1, clientArea.y + clientArea.height - 1); Point newPoint = UIUtils.getDisplay().map(SnippetItem.this, getParent(), new Point(clientArea.x, clientArea.y)); if (newPoint.y == 0) { e.gc.drawLine(clientArea.x - SNIPPET_MARGIN, clientArea.y, clientArea.x + clientArea.width + SNIPPET_MARGIN, clientArea.y); } e.gc.setForeground(foreground); } }); addMouseListener(mouseListener); addThemedControl(this, SWT.NONE, -1); addTitleAndDescriptionForSnippet(snippetData.snippet, this, mouseListener); addActionToobarForSnippet(snippetData.snippet, this); addTagToolbarForSnippet(snippetData.snippet, this); applyVisualEnablement(); setBackground(getBackgroundColor()); } protected void setFiltered(boolean filtered) { snippetData.filtered = filtered; applyFiltered(); } protected void applyFiltered() { setVisible(!snippetData.filtered); ((GridData) getLayoutData()).exclude = snippetData.filtered; } /** * Enablement for the snippets is mostly visual. We still want to allow users to operate on snippets even though * they my not be compatible with the current scope * * @param enabled */ protected void setVisualEnabled(boolean enabled) { snippetData.visuallyEnabled = enabled; applyVisualEnablement(); } protected void applyVisualEnablement() { if (snippetData.visuallyEnabled) { titleLabel.setForeground(themeFgColor); } else { titleLabel.setForeground(descLabel.getForeground()); } if (tagToolBar != null && tagToolBar.isDisposed()) { tagToolBar.setEnabled(snippetData.visuallyEnabled); } imageLabel.setImage(getSnippetImage(snippetData.visuallyEnabled)); } private void addTitleAndDescriptionForSnippet(SnippetElement snippet, Composite itemComposite, MouseListener mouseListener) { imageLabel = new Label(itemComposite, SWT.NONE); imageLabel.setLayoutData(GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).create()); imageLabel.setImage(getSnippetImage(true)); imageLabel.addMouseListener(mouseListener); imageLabel.setBackground(getBackgroundColor()); addThemedControl(imageLabel, SWT.NONE, -1); textComposite = new Composite(itemComposite, SWT.NONE); textComposite.setLayout(GridLayoutFactory.fillDefaults().spacing(3, 0).numColumns(2).create()); textComposite.setLayoutData( GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.CENTER).grab(true, false).create()); textComposite.addMouseListener(mouseListener); textComposite.setBackground(getBackgroundColor()); addThemedControl(textComposite, SWT.NONE, -1); titleLabel = new CLabel(textComposite, SWT.NONE); titleLabel.setText(snippet.getDisplayName()); titleLabel.setLayoutData( GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.CENTER).grab(false, false).create()); titleLabel.addMouseListener(mouseListener); titleLabel.setBackground(getBackgroundColor()); addThemedControl(titleLabel, SWT.BOLD, -1); descLabel = new CLabel(textComposite, SWT.NONE); String descText = snippet.getDescription(); if (descText == null || StringUtil.isEmpty(descText)) { descText = descriptionReplacePattern.matcher(snippet.getExpansion()).replaceAll(" "); //$NON-NLS-1$ } descLabel.setText(descText); descLabel.setLayoutData( GridDataFactory.fillDefaults().align(SWT.BEGINNING, SWT.CENTER).grab(true, false).create()); descLabel.addMouseListener(mouseListener); descLabel.setBackground(getBackgroundColor()); addThemedControl(descLabel, SWT.NONE, 128); addDragDropForSnippet(); } private void addActionToobarForSnippet(final SnippetElement snippet, final Composite itemComposite) { toolbar = new ToolBar(itemComposite, SWT.HORIZONTAL); toolbar.setLayoutData(GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER).create()); toolbar.setVisible(false); toolbar.setBackground(getBackgroundColor()); addThemedControl(toolbar, SWT.NONE, -1); ToolItem insertItem = new ToolItem(toolbar, SWT.NONE); insertItem.setImage(insertSnippetImage); insertItem.setToolTipText(Messages.SnippetsView_Insert_Snippet_desc); insertItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { insertSnippet(snippet); } }); ToolItem infoItem = new ToolItem(toolbar, SWT.NONE); infoItem.setImage(informationImage); infoItem.setToolTipText(Messages.SnippetsView_Show_Information_desc); infoItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { closeSnippetDialog(); snippetDialog = new SnippetPopupDialog(getShell(), snippet, toolbar); snippetDialog.open(); } }); itemComposite.addMouseMoveListener(new MouseMoveListener() { public void mouseMove(MouseEvent e) { boolean contains = itemComposite.getClientArea().contains(e.x, e.y); if (contains) { for (SnippetItem item : snippetItems.values()) { ToolBar tempToolbar = item.toolbar; if (tempToolbar != toolbar) { tempToolbar.setVisible(false); } } } toolbar.setVisible(contains); } }); } private void addTagToolbarForSnippet(final SnippetElement snippet, final Composite itemComposite) { List<String> tags = snippet.getTags(); if (!CollectionsUtil.isEmpty(tags)) { new Label(itemComposite, SWT.NONE); tagToolBar = new ToolBar(itemComposite, SWT.HORIZONTAL | SWT.WRAP); tagToolBar.setLayoutData(GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER).grab(true, false) .span(2, 1).create()); tagToolBar.setBackground(getBackgroundColor()); addThemedControl(tagToolBar, SWT.NONE, -1); int height = -1; for (String tag : tags) { height = createTagToolItem(tagToolBar, height, tag); } } } private void addDragDropForSnippet() { // Add DnD int operations = DND.DROP_COPY; Transfer[] types = new Transfer[] { SnippetTransfer.getInstance() }; DragSourceListener listener = new DragSourceListener() { public void dragStart(DragSourceEvent event) { event.doit = true; event.image = insertSnippetImage; } public void dragSetData(DragSourceEvent event) { event.data = snippetData.snippet; } public void dragFinished(DragSourceEvent event) { } }; Control[] controls = new Control[] { imageLabel, titleLabel, descLabel }; for (Control control : controls) { DragSource titleSource = new DragSource(control, operations); titleSource.setTransfer(types); titleSource.addDragListener(listener); } } private Image getSnippetImage(boolean enabled) { URL iconURL = snippetData.snippet.getIconURL(); Image image = null; if (enabled) { image = iconURL != null ? getImage(iconURL) : genericSnippetImage; } else { image = iconURL != null ? getDisabledImage(iconURL) : genericDisabledSnippetImage; } return image; } } /** * Job used to show/hide snippets * * @author nle */ private class FilterSnippetsJob extends Job { List<SnippetData> snippets = new ArrayList<SnippetData>(); public FilterSnippetsJob(List<SnippetData> newSnippets) { super("Filter snippets"); //$NON-NLS-1$ snippets.addAll(newSnippets); } @Override protected IStatus run(IProgressMonitor monitor) { Display.getDefault().asyncExec(new Runnable() { public void run() { showSnippets(snippets); } }); return Status.OK_STATUS; } } FilterSnippetsJob filterSnippetsJob = null; private IPartListener partListener = new IPartListener() { public void partOpened(IWorkbenchPart part) { // No-op } public void partDeactivated(IWorkbenchPart part) { // No-op } public void partClosed(IWorkbenchPart part) { // No-op } public void partBroughtToTop(IWorkbenchPart part) { // No-op } public void partActivated(IWorkbenchPart part) { updateBasedOnPart(part); } }; public SnippetsView() { setPartName(Messages.SnippetsView_partName); imageRegistry = new ImageRegistry(); disabledImageRegistry = new ImageRegistry(); hotTagImageRegistry = new ImageRegistry(); colorManager = new ColorManager(); themeFactory = ThemePlugin.getDefault().getControlThemerFactory(); snippetBundleListener = new SnippetBundleListener(); } private void updateThemeFont() { boolean isInvasiveFont = Platform.getPreferencesService().getBoolean(ThemePlugin.PLUGIN_ID, IPreferenceConstants.INVASIVE_FONT, false, null); if (isInvasiveFont) { themeFont = getFont(); } else { themeFont = defaultFont; } } private void updateThemeColors() { updateThemeFont(); if (ThemePlugin.applyToViews()) { themeBgColor = ThemePlugin.getDefault().getColorManager() .getColor(ThemePlugin.getDefault().getThemeManager().getCurrentTheme().getBackground()); themeFgColor = ThemePlugin.getDefault().getColorManager() .getColor(ThemePlugin.getDefault().getThemeManager().getCurrentTheme().getForeground()); themeSelectionColor = ThemePlugin.getDefault().getColorManager() .getColor(ThemePlugin.getDefault().getThemeManager().getCurrentTheme().getSelectionAgainstBG()); } else { themeBgColor = null; themeFgColor = null; themeSelectionColor = null; } updateBgColor(); // Update tag images int height = -1; for (String tagName : toolItemMap.keySet()) { List<ToolItem> toolItems = toolItemMap.get(tagName); for (ToolItem toolItem : toolItems) { toolItem.setImage(null); toolItem.setHotImage(null); } imageRegistry.remove(tagName); hotTagImageRegistry.remove(tagName); for (ToolItem toolItem : toolItems) { toolItem.getParent().setBackground(getBackgroundColor()); height = createTagImagesForToolItem(toolItem.getParent(), height, tagName, toolItem); } } for (SnippetItem item : snippetItems.values()) { item.applyVisualEnablement(); } updateSnippetDrawers(); } protected Font getFont() { Font font = JFaceResources.getFont(IThemeManager.VIEW_FONT_NAME); if (font == null) { font = JFaceResources.getTextFont(); } return font; } synchronized private void closeSnippetDialog() { if (snippetDialog != null) { snippetDialog.close(); snippetDialog = null; } } /** * Create contents of the view part. * * @param parent */ @Override public void createPartControl(final Composite parent) { filter = new SnippetsFilter(); defaultFont = parent.getFont(); updateThemeColors(); themeListener = new IPreferenceChangeListener() { public void preferenceChange(PreferenceChangeEvent event) { String key = event.getKey(); if (IThemeManager.THEME_CHANGED.equals(key) || IPreferenceConstants.APPLY_TO_ALL_VIEWS.equals(key) || IPreferenceConstants.INVASIVE_FONT.equals(key)) { updateThemeColors(); } } }; parent.setLayout(GridLayoutFactory.fillDefaults().margins(0, 1).spacing(0, 0).create()); search = new SearchComposite(parent, SWT.NONE, false, new SearchComposite.Client() { @SuppressWarnings("restriction") public void search(String text, boolean isCaseSensitive, boolean isRegularExpression) { Pattern pattern = PatternConstructor.createPattern(text, isCaseSensitive, isRegularExpression); filter.setPattern(pattern); List<SnippetElement> snippets = new ArrayList<SnippetElement>(); Map<SnippetElement, SnippetData> mapping = new HashMap<SnippetElement, SnippetsView.SnippetData>(); for (List<SnippetData> list : sortedSnippets.values()) { for (SnippetData data : list) { snippets.add(data.snippet); mapping.put(data.snippet, data); } } Object[] filtered = filter.filter(null, (Object) null, snippets.toArray(new SnippetElement[snippets.size()])); List<SnippetData> snippetDatas = new ArrayList<SnippetsView.SnippetData>(); for (Object object : filtered) { snippetDatas.add(mapping.get(object)); } // Updates the snippets on a delay if (filterSnippetsJob != null) { filterSnippetsJob.cancel(); } filterSnippetsJob = new FilterSnippetsJob(snippetDatas); filterSnippetsJob.schedule(750); } }); search.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create()); search.setSearchOnEnter(false); search.setInitialText(Messages.SnippetsView_Initial_filter_text); scrolledComposite = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); scrolledComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); scrolledComposite.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).create()); scrolledComposite.getVerticalBar().setIncrement(6); expandBar = new ExpandBar(scrolledComposite, SWT.NONE); expandBar.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create()); scrolledComposite.setBackground(getBackgroundColor()); scrolledComposite.setForeground(themeFgColor); scrolledComposite.setContent(expandBar); scrolledComposite.setExpandHorizontal(true); scrolledComposite.setExpandVertical(true); updateBgColor(); createSnippetDrawers(); expandBar.addExpandListener(new ExpandListener() { public void itemExpanded(ExpandEvent e) { expandDrawer((String) ((ExpandItem) e.item).getData(), false); updateScrollMinSize((ExpandItem) e.item, true); collapsedCategories.remove((String) e.item.getData()); } public void itemCollapsed(ExpandEvent e) { updateScrollMinSize((ExpandItem) e.item, false); collapsedCategories.add((String) e.item.getData()); } }); initializeToolBar(); IPartService service = (IPartService) getSite().getService(IPartService.class); if (service != null) { service.addPartListener(partListener); } IWorkbenchPage page = getSite().getPage(); if (page != null) { updateBasedOnPart(page.getActiveEditor()); } getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(selectionListener); } private void updateBasedOnPart(IWorkbenchPart part) { if (part instanceof AbstractThemeableEditor) { AbstractThemeableEditor textEditor = (AbstractThemeableEditor) part; String scope = null; try { ISourceViewer viewer = TextEditorUtils.getSourceViewer(textEditor); int caretOffset = textEditor.getCaretOffset(); if (viewer == null) { IDocument document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()); scope = CommonEditorPlugin.getDefault().getDocumentScopeManager().getScopeAtOffset(document, caretOffset); } else { // Get the scope at caret offset scope = CommonEditorPlugin.getDefault().getDocumentScopeManager().getScopeAtOffset(viewer, caretOffset); } } catch (Exception e) { } if (currentScope == null || !currentScope.equals(scope)) { updateSnippetEnablement(scope); } } else if (part instanceof IEditorPart) { updateSnippetEnablement(null); } } /* * Updates the minimum size of the scrolled composite after a drawer is expanded/collapsed. This ensures the * scrolling works properly */ private void updateScrollMinSize(ExpandItem item, boolean expanded) { if (item == null) { Point size = expandBar.computeSize(SWT.DEFAULT, SWT.DEFAULT); scrolledComposite.setMinSize(size.x, size.y); } else { int minHeight = scrolledComposite.getMinHeight(); minHeight = expanded ? minHeight + item.getHeight() : minHeight - item.getHeight(); scrolledComposite.setMinHeight(minHeight); } } protected void showSnippets(List<SnippetData> snippets) { Map<String, List<SnippetElement>> localSnippets = new HashMap<String, List<SnippetElement>>(); for (SnippetData snippetData : snippets) { String category = getSnippetCategoryName(snippetData.snippet); List<SnippetElement> snippetList = localSnippets.get(category); if (snippetList == null) { snippetList = new ArrayList<SnippetElement>(); localSnippets.put(category, snippetList); } snippetList.add(snippetData.snippet); } // Determine whether to show or hide the snippet Map<String, List<Control>> changedSnippets = new HashMap<String, List<Control>>(); for (String category : sortedSnippets.keySet()) { List<SnippetData> datas = sortedSnippets.get(category); for (SnippetData data : datas) { SnippetItem item = snippetItems.get(data); boolean newExclude = !snippets.contains(data); boolean oldExclude = data.filtered; data.filtered = newExclude; boolean itemChanged = false; if (item != null) { if (newExclude != oldExclude) { itemChanged = true; } item.setFiltered(newExclude); } else if (!newExclude) { ExpandItem expandItem = expandItems.get(category); if (expandItem.getExpanded()) { expandDrawer(category, false); itemChanged = true; item = snippetItems.get(data); if (item != null) { item.setFiltered(newExclude); } } } if (itemChanged) { List<Control> list = changedSnippets.get(category); if (list == null) { list = new ArrayList<Control>(); changedSnippets.put(category, list); } list.add(item); } } } // Update category text and layout if necessary for (String category : expandItems.keySet()) { int size = 0; List<SnippetElement> sortedSnippets = localSnippets.get(category); if (sortedSnippets != null) { size = sortedSnippets.size(); } ExpandItem expandItem = expandItems.get(category); expandItem.setText(MessageFormat.format(Messages.SnippetsView_Snippet_drawer_title, category != null ? category : Messages.SnippetsView_Snippet_drawer_other, String.valueOf(size))); List<Control> list = changedSnippets.get(category); if (!CollectionsUtil.isEmpty(list)) { ((Composite) expandItem.getControl()).layout(list.toArray(new Control[list.size()])); expandItem.setHeight(expandItem.getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT).y); } } updateScrollMinSize(null, false); } protected String getSnippetCategoryName(SnippetElement snippet) { return snippet.getCategory() != null ? snippet.getCategory() : snippet.getOwningBundle().getDisplayName(); } protected void createSnippetDrawers() { disposeThemers(); themedControls.clear(); snippetItems.clear(); toolItemMap.clear(); currentScope = null; EclipseUtil.instanceScope().getNode(ThemePlugin.PLUGIN_ID).removePreferenceChangeListener(themeListener); List<String> expandedDrawers = new ArrayList<String>(); for (ExpandItem item : expandItems.values()) { if (item.isDisposed()) { continue; } else if (item.getExpanded()) { expandedDrawers.add((String) item.getData()); } item.getControl().dispose(); item.dispose(); } expandItems.clear(); updateCategoriesAndSnippets(expandedDrawers); updateScrollMinSize(null, false); scrolledComposite.layout(true, true); updateSnippetDrawers(); EclipseUtil.instanceScope().getNode(ThemePlugin.PLUGIN_ID).addPreferenceChangeListener(themeListener); } /** * Lazy load the create of SnippetItems when the category is expanded * * @param category * @param shouldExpand */ private void expandDrawer(String category, boolean shouldExpand) { ExpandItem expandItem = expandItems.get(category); if (expandItem != null) { boolean adjustHeight = false; Composite composite = (Composite) expandItem.getControl(); List<SnippetData> elements = sortedSnippets.get(category); if (!CollectionsUtil.isEmpty(elements)) { for (SnippetData element : elements) { if (snippetItems.get(element) == null) { snippetItems.put(element, new SnippetItem(composite, element)); adjustHeight = true; } } } if (adjustHeight) { expandItem.setHeight(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); } if (shouldExpand) { expandItem.setExpanded(true); } } } private void updateSnippetEnablement(String scope) { currentScope = scope; for (List<SnippetData> datas : sortedSnippets.values()) { for (SnippetData data : datas) { boolean matches = data.snippet.getScopeSelector().matches(scope); SnippetItem item = snippetItems.get(data); if (item != null) { item.setVisualEnabled(matches); } else { data.visuallyEnabled = matches; } } } updateSnippetDrawers(); } private void updateCategoriesAndSnippets(List<String> expandedDrawers) { snippetCategories.clear(); for (SnippetCategoryElement category : BundleManager.getInstance().getSnippetCategories(null)) { snippetCategories.put(category.getDisplayName(), category); } List<SnippetElement> snippets = BundleManager.getInstance().getSnippets(null); if (CollectionsUtil.isEmpty(snippets)) { currentScope = null; } snippetItems.clear(); sortedSnippets.clear(); toolItemMap.clear(); Set<String> tags = new HashSet<String>(); for (SnippetElement snippet : snippets) { String category = getSnippetCategoryName(snippet); if (!snippetCategories.containsKey(category)) { SnippetCategoryElement snippetCategoryElement = new SnippetCategoryElement(null); snippetCategoryElement.setDisplayName(category); snippetCategories.put(category, snippetCategoryElement); } List<SnippetData> snippetList = sortedSnippets.get(category); if (snippetList == null) { snippetList = new ArrayList<SnippetData>(); sortedSnippets.put(category, snippetList); } snippetList.add(new SnippetData(snippet)); tags.addAll(snippet.getTags()); } // Only on windows: Ensure the image widths are all the same for tags, due to the platform scaling all toolitems // to be the same width if (Platform.OS_WIN32.equals(Platform.getOS())) { tagWidth = -1; GC gc = new GC(UIUtils.getDisplay()); if (!CollectionsUtil.isEmpty(tags)) { for (String tag : tags) { if (!StringUtil.isEmpty(tag)) { int extent = gc.textExtent(MessageFormat.format("__{0}__", tag)).x; //$NON-NLS-1$ if (extent > tagWidth) { tagWidth = extent; } } } } gc.dispose(); } List<SnippetCategoryElement> categories = new ArrayList<SnippetCategoryElement>(); categories.addAll(snippetCategories.values()); Collections.sort(categories, snippetCategoryElementComparator); for (SnippetCategoryElement category : categories) { List<SnippetData> snippetList = sortedSnippets.get(category.getDisplayName()); ExpandItem expandItem = createSnippetDrawer(expandBar, category, snippetList); if (expandedDrawers.contains(expandItem.getData())) { expandDrawer((String) expandItem.getData(), true); } } } private void addThemedControl(Control control, int fontStyle, int alpha) { themedControls.add(control); Color defaultBg = UIUtils.getDisplay().getSystemColor(SWT.COLOR_WHITE); if (fontStyle != SWT.NONE) { themeFactory.applyWithFontStyle(control, fontStyle, defaultBg); } else if (alpha > -1) { themeFactory.applyWithAlpha(control, alpha, defaultBg); } else { themeFactory.apply(control, defaultBg); } } private void disposeThemers() { for (Control control : themedControls) { themeFactory.dispose(control); } } private void updateBgColor() { if (scrolledComposite != null && expandBar != null) { scrolledComposite.setBackground(getBackgroundColor()); expandBar.setBackground(getBackgroundColor()); } } private Color getBackgroundColor() { return themeBgColor != null ? themeBgColor : UIUtils.getDisplay().getSystemColor(SWT.COLOR_WHITE); } private void updateSnippetDrawers() { for (ExpandItem item : expandItems.values()) { Composite control = (Composite) item.getControl(); control.layout(true, true); item.setHeight(control.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); } } protected ExpandItem createSnippetDrawer(ExpandBar expandBar, SnippetCategoryElement category, List<SnippetData> snippets) { Composite composite = new Composite(expandBar, SWT.NONE); composite.setForeground( themeFgColor != null ? themeFgColor : UIUtils.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BORDER)); composite.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).create()); ExpandItem expandItem = new ExpandItem(expandBar, SWT.NONE); int size = 0; if (snippets != null) { size = snippets.size(); } expandItem.setText(MessageFormat.format(Messages.SnippetsView_Snippet_drawer_title, category.getDisplayName(), String.valueOf(size))); expandItem.setControl(composite); expandItem.setImage(category != null && category.getIconURL() != null ? getImage(category.getIconURL()) : genericSnippetImage); expandItem.setData(category != null ? category.getDisplayName() : null); expandItem.setHeight(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); expandItems.put(category != null ? category.getDisplayName() : null, expandItem); return expandItem; } private void insertSnippet(SnippetElement snippet) { IEditorPart activeEditor = UIUtils.getActiveEditor(); ITextEditor textEditor = null; if (activeEditor instanceof MultiPageEditorPart) { Object selectedPage = ((MultiPageEditorPart) activeEditor).getSelectedPage(); if (selectedPage instanceof ITextEditor) { textEditor = (ITextEditor) selectedPage; } } else if (activeEditor instanceof ITextEditor) { textEditor = (ITextEditor) activeEditor; } if (textEditor != null) { CommandResult commandResult = CommandExecutionUtils.executeCommand(snippet, InvocationType.MENU, textEditor); if (commandResult != null) { CommandExecutionUtils.processCommandResult(snippet, commandResult, textEditor); activeEditor.setFocus(); } } } private int createTagToolItem(ToolBar tagToolBar, int height, final String tag) { ToolItem item = new ToolItem(tagToolBar, SWT.PUSH); item.setData("tag", tag); //$NON-NLS-1$ height = createTagImagesForToolItem(tagToolBar, height, tag, item); item.addSelectionListener(new SelectionAdapter() { /* * (non-Javadoc) * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e) { search.getTextControl().setFocus(); search.getTextControl().setText(tag); } }); List<ToolItem> toolItems = toolItemMap.get(tag); if (toolItems == null) { toolItems = new ArrayList<ToolItem>(); toolItemMap.put(tag, toolItems); } toolItems.add(item); return height; } private int createTagImagesForToolItem(ToolBar tagToolBar, int height, String tag, ToolItem item) { item.setHotImage(null); item.setImage(null); item.setText(MessageFormat.format("__{0}__", tag)); //$NON-NLS-1$ Rectangle bounds = item.getBounds(); int width = bounds.width; item.setText(StringUtil.EMPTY); if (height < 0) { GC gc = new GC(Display.getCurrent()); gc.setFont(themeFont); Point textExtent = gc.textExtent("apt", SWT.DRAW_TRANSPARENT); //$NON-NLS-1$ height = textExtent.y + (textExtent.y % 2 == 0 ? 4 : 5); gc.dispose(); } Image image = imageRegistry.get(tag); if (image == null) { image = createTagImage(tagToolBar, height, width, tag, false); } item.setImage(image); Image hotImage = hotTagImageRegistry.get(tag); if (hotImage == null) { hotImage = createTagImage(tagToolBar, height, width, tag, true); } item.setHotImage(hotImage); return height; } private Image createTagImage(ToolBar tagToolBar, int height, int width, String tagName, boolean isHot) { if (tagWidth > 0) { width = tagWidth; } Image image = new Image(UIUtils.getDisplay(), width, height); GC gc = new GC(image); gc.setAntialias(SWT.ON); Color fg = gc.getForeground(); Color bgColor = tagToolBar.getBackground(); if (themeBgColor != null) { bgColor = themeBgColor; } gc.setFont(themeFont); Point textSize = gc.textExtent(tagName); gc.setBackground(bgColor); gc.fillRectangle(0, 0, width, height); Color tempFg = null; Color tempBg = null; if (isHot) { if (themeFgColor == null) { tempFg = colorManager.getColor(defaultTagHotFg); tempBg = colorManager.getColor(defaultTagHotBg); } else { tempFg = colorManager .getColor(Theme.alphaBlend(themeBgColor.getRGB(), themeFgColor.getRGB(), FG_ALPHA)); tempBg = themeSelectionColor; } } else { if (themeFgColor == null) { tempFg = colorManager.getColor(defaultTagFg); tempBg = colorManager.getColor(defaultTagBg); } else { tempFg = colorManager .getColor(Theme.alphaBlend(themeBgColor.getRGB(), themeFgColor.getRGB(), FG_ALPHA)); tempBg = colorManager .getColor(Theme.alphaBlend(themeBgColor.getRGB(), themeSelectionColor.getRGB(), FG_ALPHA)); } } gc.setForeground(tempFg); gc.setBackground(tempBg); gc.setAntialias(SWT.OFF); gc.fillRoundRectangle(1, 1, width - 2, height - 2, height - 2, height - 2); gc.setAntialias(SWT.ON); gc.drawRoundRectangle(0, 0, width - 1, height - 1, height - 1, height - 1); gc.setForeground(themeFgColor == null ? fg : themeFgColor); gc.drawText(tagName, (width - textSize.x) / 2, (height - textSize.y) / 2, true); ImageData imageData = image.getImageData(); imageData.transparentPixel = imageData.palette.getPixel(bgColor.getRGB()); image.dispose(); gc.dispose(); image = new Image(UIUtils.getDisplay(), imageData); ImageRegistry registry = isHot ? hotTagImageRegistry : imageRegistry; registry.put(tagName, image); return image; } private Image getImage(URL url) { String urlString = url.toString(); Image image = imageRegistry.get(urlString); if (image == null) { imageRegistry.put(urlString, ImageDescriptor.createFromURL(url)); image = imageRegistry.get(urlString); } return image; } private Image getDisabledImage(URL url) { String urlString = url.toString(); Image image = disabledImageRegistry.get(urlString); if (image == null) { ImageDescriptor descriptor = imageRegistry.getDescriptor(urlString); disabledImageRegistry.put(urlString, ImageDescriptor.createWithFlags(descriptor, SWT.IMAGE_DISABLE)); image = disabledImageRegistry.get(urlString); } return image; } /** * Initialize the toolbar. */ private void initializeToolBar() { IToolBarManager toolbarManager = getViewSite().getActionBars().getToolBarManager(); Action collapseAll = new Action() { /* * (non-Javadoc) * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { for (ExpandItem item : expandItems.values()) { boolean expanded = item.getExpanded(); item.setExpanded(false); if (expanded) { updateScrollMinSize(item, false); } } } }; collapseAll.setText(Messages.SnippetsView_Collapse_All_Action); collapseAll.setImageDescriptor( PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL)); toolbarManager.add(collapseAll); Action expandAll = new Action() { /* * (non-Javadoc) * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { for (ExpandItem item : expandItems.values()) { boolean expanded = item.getExpanded(); expandDrawer((String) item.getData(), true); if (!expanded) { updateScrollMinSize(item, true); } } } }; expandAll.setText(Messages.SnippetsView_Expand_All_Action); expandAll.setImageDescriptor(ScriptingUIPlugin.getImageDescriptor(ICONS_FULL_ELCL16_EXPANDALL_GIF)); toolbarManager.add(expandAll); } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ @Override public void dispose() { imageRegistry.dispose(); disabledImageRegistry.dispose(); hotTagImageRegistry.dispose(); EclipseUtil.instanceScope().getNode(ThemePlugin.PLUGIN_ID).removePreferenceChangeListener(themeListener); snippetBundleListener.dispose(); super.dispose(); colorManager.dispose(); disposeThemers(); getSite().getWorkbenchWindow().getSelectionService().removePostSelectionListener(selectionListener); IPartService service = (IPartService) getSite().getService(IPartService.class); if (service != null) { service.removePartListener(partListener); } } @Override public void setFocus() { expandBar.setFocus(); } }