| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.team.internal.ccvs.ui.tags; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IMenuListener; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.operation.IRunnableContext; |
| import org.eclipse.jface.viewers.ColumnWeightData; |
| import org.eclipse.jface.viewers.ISelectionChangedListener; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.SelectionChangedEvent; |
| import org.eclipse.jface.viewers.StructuredSelection; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.TableLayout; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.KeyListener; |
| import org.eclipse.swt.events.ModifyEvent; |
| import org.eclipse.swt.events.ModifyListener; |
| import org.eclipse.swt.events.MouseAdapter; |
| import org.eclipse.swt.events.MouseEvent; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Table; |
| import org.eclipse.swt.widgets.TableColumn; |
| import org.eclipse.swt.widgets.TableItem; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.team.internal.ccvs.core.CVSTag; |
| import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation; |
| import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin; |
| import org.eclipse.team.internal.ccvs.ui.Policy; |
| import org.eclipse.team.internal.ccvs.ui.actions.CVSAction; |
| import org.eclipse.team.internal.ccvs.ui.repo.NewDateTagAction; |
| import org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager; |
| import org.eclipse.team.internal.ccvs.ui.tags.TagSourceWorkbenchAdapter.ProjectElementSorter; |
| import org.eclipse.team.internal.ui.PixelConverter; |
| import org.eclipse.team.internal.ui.SWTUtils; |
| import org.eclipse.team.internal.ui.dialogs.DialogArea; |
| import org.eclipse.ui.help.WorkbenchHelp; |
| import org.eclipse.ui.model.WorkbenchContentProvider; |
| import org.eclipse.ui.model.WorkbenchLabelProvider; |
| import org.eclipse.ui.part.PageBook; |
| |
| /** |
| * A dialog area that displays a list of tags for selection and supports |
| * filtering of the displayed tags. |
| */ |
| public class TagSelectionArea extends DialogArea { |
| |
| /* |
| * Property constant which identifies the selected tag or |
| * null if no tag is selected |
| */ |
| public static final String SELECTED_TAG = "selectedTag"; //$NON-NLS-1$ |
| |
| /* |
| * Property constant which indicates that a tag has been selected in such |
| * a way as to indicate that this is the desired tag (e.g double-click) |
| */ |
| public static final String OPEN_SELECTED_TAG = "openSelectedTag"; //$NON-NLS-1$ |
| |
| /* |
| * Constants used to configure which tags are shown |
| */ |
| public static final int INCLUDE_HEAD_TAG = TagSourceWorkbenchAdapter.INCLUDE_HEAD_TAG; |
| public static final int INCLUDE_BASE_TAG = TagSourceWorkbenchAdapter.INCLUDE_BASE_TAG; |
| public static final int INCLUDE_BRANCHES = TagSourceWorkbenchAdapter.INCLUDE_BRANCHES; |
| public static final int INCLUDE_VERSIONS = TagSourceWorkbenchAdapter.INCLUDE_VERSIONS; |
| public static final int INCLUDE_DATES = TagSourceWorkbenchAdapter.INCLUDE_DATES; |
| public static final int INCLUDE_ALL_TAGS = TagSourceWorkbenchAdapter.INCLUDE_ALL_TAGS; |
| |
| private String tagAreaLabel; |
| private final int includeFlags; |
| private CVSTag selection; |
| private String helpContext; |
| private Text filterText; |
| private TagSource tagSource; |
| private Label tagAreaTextLabel; |
| private final Shell shell; |
| private TagRefreshButtonArea tagRefreshArea; |
| private final TagSource.ITagSourceChangeListener listener = new TagSource.ITagSourceChangeListener() { |
| public void tagsChanged(TagSource source) { |
| Shell shell = getShell(); |
| if (!shell.isDisposed()) { |
| shell.getDisplay().syncExec(new Runnable() { |
| public void run() { |
| refresh(); |
| } |
| }); |
| } |
| } |
| }; |
| private final DisposeListener disposeListener = new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| if (tagSource != null) |
| tagSource.removeListener(listener); |
| } |
| }; |
| |
| private PageBook switcher; |
| private TreeViewer tagTree; |
| private TableViewer tagTable; |
| private boolean treeVisible = true; |
| private boolean includeFilterInputArea = true; |
| private String filterPattern = ""; //$NON-NLS-1$ |
| |
| private IRunnableContext context; |
| |
| public TagSelectionArea(Shell shell, TagSource tagSource, int includeFlags, String helpContext) { |
| this.shell = shell; |
| this.includeFlags = includeFlags; |
| this.helpContext = helpContext; |
| this.tagSource = tagSource; |
| setSelection(null); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.team.internal.ui.dialogs.DialogArea#createArea(org.eclipse.swt.widgets.Composite) |
| */ |
| public void createArea(Composite parent) { |
| initializeDialogUnits(parent); |
| Dialog.applyDialogFont(parent); |
| final PixelConverter converter= new PixelConverter(parent); |
| |
| // Create a composite for the entire area |
| Composite composite= new Composite(parent, SWT.NONE); |
| composite.setLayoutData(SWTUtils.createHVFillGridData()); |
| composite.setLayout(SWTUtils.createGridLayout(1, converter, SWTUtils.MARGINS_NONE)); |
| |
| // Add F1 help |
| if (helpContext != null) { |
| WorkbenchHelp.setHelp(composite, helpContext); |
| } |
| |
| // Create the tree area and refresh buttons with the possibility to add stuff in between |
| createTagDisplayArea(composite); |
| createCustomArea(composite); |
| createRefreshButtons(composite); |
| |
| Dialog.applyDialogFont(parent); |
| updateTagDisplay(true); |
| } |
| |
| private void createTagDisplayArea(Composite parent) { |
| Composite inner = createGrabbingComposite(parent, 1); |
| if (isIncludeFilterInputArea()) { |
| createFilterInput(inner); |
| tagAreaTextLabel = createWrappingLabel(inner, Policy.bind("TagSelectionArea.0"), 1); //$NON-NLS-1$ |
| } else { |
| tagAreaTextLabel = createWrappingLabel(inner, Policy.bind("TagSelectionArea.1", getTagAreaLabel()), 1); //$NON-NLS-1$ |
| } |
| switcher = new PageBook(inner, SWT.NONE); |
| GridData gridData = new GridData(GridData.FILL_BOTH); |
| gridData.heightHint = 0; |
| gridData.widthHint = 0; |
| switcher.setLayoutData(gridData); |
| tagTree = createTree(switcher); |
| tagTable = createTable(switcher); |
| } |
| |
| private void createFilterInput(Composite inner) { |
| createWrappingLabel(inner, Policy.bind("TagSelectionArea.2", getTagAreaLabel()), 1); //$NON-NLS-1$ |
| filterText = createText(inner, 1); |
| filterText.addModifyListener(new ModifyListener() { |
| public void modifyText(ModifyEvent e) { |
| setFilter(filterText.getText()); |
| } |
| }); |
| filterText.addKeyListener(new KeyListener() { |
| public void keyPressed(KeyEvent e) { |
| if (e.keyCode == SWT.ARROW_DOWN && e.stateMask == 0) { |
| tagTable.getControl().setFocus(); |
| } |
| } |
| public void keyReleased(KeyEvent e) { |
| // Ignore |
| } |
| }); |
| } |
| |
| /** |
| * Return the label that should be used for the tag area. |
| * It should not have any trailing punctuations as the tag area |
| * may position it differently depending on whether the filter |
| * text input is included in the area. |
| * @return the tag area label |
| */ |
| public String getTagAreaLabel() { |
| if (tagAreaLabel == null) |
| tagAreaLabel = Policy.bind("TagSelectionArea.3"); //$NON-NLS-1$ |
| return tagAreaLabel; |
| } |
| |
| /** |
| * Set the label that should be used for the tag area. |
| * It should not have any trailing punctuations as the tag area |
| * may position it differently depending on whether the filter |
| * text input is included in the area. |
| * @param tagAreaLabel the tag area label |
| */ |
| public void setTagAreaLabel(String tagAreaLabel) { |
| this.tagAreaLabel = tagAreaLabel; |
| } |
| |
| /** |
| * Update the tag display to show the tags that match the |
| * include flags and the filter entered by the user. |
| */ |
| protected void updateTagDisplay(boolean firstTime) { |
| String filter = getFilterString(); |
| if ((filter != null && filter.length() > 0) || isTableOnly()) { |
| // Show the table and filter it accordingly |
| try { |
| switcher.setRedraw(false); |
| treeVisible = false; |
| switcher.showPage(tagTable.getControl()); |
| FilteredTagList list = (FilteredTagList)tagTable.getInput(); |
| list.setPattern(filter); |
| tagTable.refresh(); |
| if (filterText == null || filter == null || filter.length() == 0) { |
| setSelection(selection); |
| } else { |
| // Only set the top selection if there is a filter from the filter text |
| // of this area. This is done to avoid selection loops |
| selectTopElement(); |
| } |
| } finally { |
| switcher.setRedraw(true); |
| } |
| } else { |
| // Show the tree |
| if (!isTreeVisible() || firstTime) { |
| try { |
| switcher.setRedraw(false); |
| treeVisible = true; |
| switcher.showPage(tagTree.getControl()); |
| tagTree.refresh(); |
| setSelection(selection); |
| } finally { |
| switcher.setRedraw(true); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Return whether only a table should be used |
| * @return whether only a table should be used |
| */ |
| protected boolean isTableOnly() { |
| return (includeFlags == INCLUDE_VERSIONS) || (includeFlags == INCLUDE_BRANCHES); |
| } |
| |
| private String getFilterString() { |
| return filterPattern; |
| } |
| |
| /* |
| * Select the top element in the tag table |
| */ |
| private void selectTopElement() { |
| if (tagTable.getTable().getItemCount() > 0) { |
| TableItem item = tagTable.getTable().getItem(0); |
| tagTable.getTable().setSelection(new TableItem[] { item }); |
| tagTable.setSelection(tagTable.getSelection()); |
| } |
| } |
| |
| private FilteredTagList createFilteredInput() { |
| return new FilteredTagList(tagSource, TagSource.convertIncludeFlaqsToTagTypes(includeFlags)); |
| } |
| |
| private Text createText(Composite parent, int horizontalSpan) { |
| Text text = new Text(parent, SWT.BORDER); |
| GridData data = new GridData(); |
| data.horizontalSpan = horizontalSpan; |
| data.horizontalAlignment = GridData.FILL; |
| data.grabExcessHorizontalSpace = true; |
| data.widthHint= 0; |
| text.setLayoutData(data); |
| return text; |
| } |
| |
| protected void createRefreshButtons(Composite parent) { |
| tagSource.addListener(listener); |
| parent.addDisposeListener(disposeListener); |
| tagRefreshArea = new TagRefreshButtonArea(shell, tagSource); |
| if (context != null) |
| tagRefreshArea.setRunnableContext(context); |
| tagRefreshArea.createArea(parent); |
| } |
| |
| protected void createTreeMenu(TreeViewer tagTree) { |
| if ((includeFlags & TagSourceWorkbenchAdapter.INCLUDE_DATES) != 0) { |
| // Create the popup menu |
| MenuManager menuMgr = new MenuManager(); |
| Tree tree = tagTree.getTree(); |
| Menu menu = menuMgr.createContextMenu(tree); |
| menuMgr.addMenuListener(new IMenuListener() { |
| public void menuAboutToShow(IMenuManager manager) { |
| addMenuItemActions(manager); |
| } |
| |
| }); |
| menuMgr.setRemoveAllWhenShown(true); |
| tree.setMenu(menu); |
| } |
| } |
| |
| /** |
| * Create aq custom area that is below the tag selection area but above the refresh busson group |
| * @param parent |
| */ |
| protected void createCustomArea(Composite parent) { |
| // No default custom area |
| } |
| |
| protected TreeViewer createTree(Composite parent) { |
| Tree tree = new Tree(parent, SWT.MULTI | SWT.BORDER); |
| GridData data = new GridData(GridData.FILL_BOTH); |
| tree.setLayoutData(data); |
| TreeViewer result = new TreeViewer(tree); |
| initialize(result); |
| result.getControl().addKeyListener(new KeyListener() { |
| public void keyPressed(KeyEvent event) { |
| handleKeyPressed(event); |
| } |
| public void keyReleased(KeyEvent event) { |
| handleKeyReleased(event); |
| } |
| }); |
| result.setInput(createUnfilteredInput()); |
| createTreeMenu(result); |
| return result; |
| } |
| |
| protected TableViewer createTable(Composite parent) { |
| Table table = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION); |
| GridData data = new GridData(GridData.FILL_BOTH); |
| table.setLayoutData(data); |
| TableLayout layout = new TableLayout(); |
| layout.addColumnData(new ColumnWeightData(100, true)); |
| table.setLayout(layout); |
| TableColumn col = new TableColumn(table, SWT.NONE); |
| col.setResizable(true); |
| TableViewer viewer = new TableViewer(table); |
| initialize(viewer); |
| viewer.setInput(createFilteredInput()); |
| return viewer; |
| |
| } |
| |
| private void initialize(StructuredViewer viewer) { |
| viewer.setContentProvider(new WorkbenchContentProvider()); |
| viewer.setLabelProvider(new WorkbenchLabelProvider()); |
| viewer.setSorter(new ProjectElementSorter()); |
| viewer.addSelectionChangedListener(new ISelectionChangedListener() { |
| public void selectionChanged(SelectionChangedEvent event) { |
| handleSelectionChange(); |
| } |
| }); |
| // select and close on double click |
| // To do: use defaultselection instead of double click |
| viewer.getControl().addMouseListener(new MouseAdapter() { |
| public void mouseDoubleClick(MouseEvent e) { |
| CVSTag tag = internalGetSelectedTag(); |
| if (tag != null) { |
| firePropertyChangeChange(OPEN_SELECTED_TAG, null, tag); |
| } |
| } |
| }); |
| } |
| |
| private Object createUnfilteredInput() { |
| return TagSourceWorkbenchAdapter.createInput(tagSource, includeFlags); |
| } |
| |
| public void handleKeyPressed(KeyEvent event) { |
| if (event.character == SWT.DEL && event.stateMask == 0) { |
| deleteDateTag(); |
| } |
| } |
| private void deleteDateTag() { |
| TagElement[] selectedDateTagElements = getSelectedDateTagElement(); |
| if (selectedDateTagElements.length == 0) return; |
| for(int i = 0; i < selectedDateTagElements.length; i++){ |
| RepositoryManager mgr = CVSUIPlugin.getPlugin().getRepositoryManager(); |
| CVSTag tag = selectedDateTagElements[i].getTag(); |
| if(tag.getType() == CVSTag.DATE){ |
| mgr.removeDateTag(getLocation(),tag); |
| } |
| } |
| tagTree.refresh(); |
| handleSelectionChange(); |
| } |
| |
| /** |
| * Returns the selected date tag elements |
| */ |
| private TagElement[] getSelectedDateTagElement() { |
| ArrayList dateTagElements = null; |
| IStructuredSelection selection = (IStructuredSelection)tagTree.getSelection(); |
| if (selection!=null && !selection.isEmpty()) { |
| dateTagElements = new ArrayList(); |
| Iterator elements = selection.iterator(); |
| while (elements.hasNext()) { |
| Object next = CVSAction.getAdapter(elements.next(), TagElement.class); |
| if (next instanceof TagElement) { |
| if(((TagElement)next).getTag().getType() == CVSTag.DATE){ |
| dateTagElements.add(next); |
| } |
| } |
| } |
| } |
| if (dateTagElements != null && !dateTagElements.isEmpty()) { |
| TagElement[] result = new TagElement[dateTagElements.size()]; |
| dateTagElements.toArray(result); |
| return result; |
| } |
| return new TagElement[0]; |
| } |
| private void addDateTag(CVSTag tag){ |
| if(tag == null) return; |
| List dateTags = new ArrayList(); |
| ICVSRepositoryLocation location = getLocation(); |
| dateTags.addAll(Arrays.asList(CVSUIPlugin.getPlugin().getRepositoryManager().getDateTags(location))); |
| if(!dateTags.contains( tag)){ |
| CVSUIPlugin.getPlugin().getRepositoryManager().addDateTag(location, tag); |
| } |
| try { |
| tagTree.getControl().setRedraw(false); |
| tagTree.refresh(); |
| setSelection(tag); |
| } finally { |
| tagTree.getControl().setRedraw(true); |
| } |
| handleSelectionChange(); |
| } |
| private void addMenuItemActions(IMenuManager manager) { |
| manager.add(new Action(Policy.bind("TagSelectionDialog.0")) { //$NON-NLS-1$ |
| public void run() { |
| CVSTag dateTag = NewDateTagAction.getDateTag(getShell(), getLocation()); |
| addDateTag(dateTag); |
| } |
| }); |
| if(getSelectedDateTagElement().length > 0){ |
| manager.add(new Action(Policy.bind("TagSelectionDialog.1")) { //$NON-NLS-1$ |
| public void run() { |
| deleteDateTag(); |
| } |
| }); |
| } |
| } |
| |
| protected void handleKeyReleased(KeyEvent event) { |
| } |
| |
| /** |
| * handle a selection change event from the visible tag display |
| * (which could be either the table or the tree). |
| */ |
| protected void handleSelectionChange() { |
| CVSTag newSelection = internalGetSelectedTag(); |
| if (selection != null && newSelection != null && selection.equals(newSelection)) { |
| // the selection hasn't change so return |
| return; |
| } |
| CVSTag oldSelection = selection; |
| selection = newSelection; |
| firePropertyChangeChange(SELECTED_TAG, oldSelection, selection); |
| } |
| |
| private CVSTag internalGetSelectedTag() { |
| IStructuredSelection selection; |
| if (isTreeVisible()) { |
| selection = (IStructuredSelection)tagTree.getSelection(); |
| } else { |
| selection = (IStructuredSelection)tagTable.getSelection(); |
| } |
| Object o = selection.getFirstElement(); |
| if (o instanceof TagElement) |
| return ((TagElement)o).getTag(); |
| return null; |
| } |
| |
| private boolean isTreeVisible() { |
| return treeVisible; |
| } |
| |
| private ICVSRepositoryLocation getLocation(){ |
| return tagSource.getLocation(); |
| } |
| public CVSTag getSelection() { |
| return selection; |
| } |
| public Shell getShell() { |
| return shell; |
| } |
| |
| /** |
| * Set the focus to the filter text widget |
| */ |
| public void setFocus() { |
| if (filterText != null) |
| filterText.setFocus(); |
| else if (switcher != null) |
| switcher.setFocus(); |
| |
| // Refresh in case tags were added since the last time the area had focus |
| refresh(); |
| } |
| |
| /** |
| * Select the given tag |
| * @param selectedTag the tag to be selected |
| */ |
| public void setSelection(CVSTag selectedTag) { |
| if (isTreeVisible()) |
| if (tagTree != null && !tagTree.getControl().isDisposed()) { |
| // TODO: Hack to instantiate the model before revealing the selection |
| tagTree.expandToLevel(2); |
| tagTree.collapseAll(); |
| // Reveal the selection |
| tagTree.reveal(new TagElement(selectedTag)); |
| tagTree.setSelection(new StructuredSelection(new TagElement(selectedTag))); |
| } |
| else |
| if (tagTable != null && !tagTable.getControl().isDisposed()) { |
| tagTable.setSelection(new StructuredSelection(new TagElement(selectedTag))); |
| } |
| } |
| |
| /** |
| * Refresh the state of the tag selection area |
| */ |
| public void refresh() { |
| if (isTreeVisible()) { |
| if (tagTree != null && !tagTree.getControl().isDisposed()) { |
| tagTree.refresh(); |
| } |
| } else { |
| if (tagTable != null && !tagTable.getControl().isDisposed()) { |
| tagTable.refresh(); |
| } |
| } |
| } |
| |
| public void refreshTagList() { |
| tagRefreshArea.refresh(true); |
| } |
| |
| /** |
| * Set the enablement state of the area |
| * @param enabled the enablement state |
| */ |
| public void setEnabled(boolean enabled) { |
| if (filterText != null) |
| filterText.setEnabled(enabled); |
| tagTree.getControl().setEnabled(enabled); |
| tagTable.getControl().setEnabled(enabled); |
| } |
| |
| /** |
| * Get the tag source for the tags being displayed |
| * @return the tag source for the tags being displayed |
| */ |
| public TagSource getTagSource() { |
| return tagSource; |
| } |
| |
| /** |
| * Set the tag source from which the displayed tags are determined |
| * @param tagSource the source of the tags being displayed |
| */ |
| public void setTagSource(TagSource tagSource) { |
| if (this.tagSource != null) { |
| this.tagSource.removeListener(listener); |
| } |
| this.tagSource = tagSource; |
| this.tagSource.addListener(listener); |
| tagRefreshArea.setTagSource(this.tagSource); |
| setTreeAndTableInput(); |
| } |
| |
| private void setTreeAndTableInput() { |
| if (tagTree != null) { |
| tagTree.setInput(createUnfilteredInput()); |
| } |
| if (tagTable != null) { |
| tagTable.setInput(createFilteredInput()); |
| } |
| |
| } |
| |
| /** |
| * Set whether the input filter text is to be included in the tag selection area. |
| * If excluded, clientscan still set the filter text directly using |
| * <code>setFilter</code>. |
| * @param include whether filter text input should be included |
| */ |
| public void setIncludeFilterInputArea(boolean include) { |
| includeFilterInputArea = include; |
| } |
| |
| /** |
| * Return whether the input filter text is to be included in the tag selection area. |
| * If excluded, clientscan still set the filter text directly using |
| * <code>setFilter</code>. |
| * @return whether filter text input should be included |
| */ |
| public boolean isIncludeFilterInputArea() { |
| return includeFilterInputArea; |
| } |
| |
| /** |
| * Set the text used to filter the tag list. |
| * @param filter the filter pattern |
| */ |
| public void setFilter(String filter) { |
| this.filterPattern = filter; |
| updateTagDisplay(false); |
| } |
| |
| public void setRunnableContext(IRunnableContext context) { |
| this.context = context; |
| if (tagRefreshArea != null) |
| tagRefreshArea.setRunnableContext(context); |
| } |
| } |