/******************************************************************************* * Copyright (c) 2006, 2008 Steffen Pingel and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Steffen Pingel - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.trac.ui.wizard; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.internal.trac.core.client.ITracClient; import org.eclipse.mylyn.internal.trac.core.model.TracSearch; import org.eclipse.mylyn.internal.trac.core.model.TracSearchFilter; import org.eclipse.mylyn.internal.trac.core.model.TracSearchFilter.CompareOperator; import org.eclipse.mylyn.internal.trac.core.util.TracUtil; import org.eclipse.mylyn.internal.trac.ui.TracUiPlugin; import org.eclipse.mylyn.tasks.core.IRepositoryQuery; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.mylyn.tasks.ui.wizards.AbstractRepositoryQueryPage; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; 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.SelectionAdapter; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; /** * @author Steffen Pingel */ public class TracFilterQueryPage extends AbstractRepositoryQueryPage { private static final String TITLE = Messages.TracFilterQueryPage_New_Trac_Query; private static final String DESCRIPTION = Messages.TracFilterQueryPage_Add_search_filters_to_define_query; private static final String TITLE_QUERY_TITLE = Messages.TracFilterQueryPage_Query_Title; private Text titleText; private Composite scrollComposite; /* Maintain order of criterions in order to be able to restore this later. */ private final Set<SearchField> visibleSearchFields = new LinkedHashSet<SearchField>(); private List<SearchField> searchFields; public TracFilterQueryPage(TaskRepository repository, IRepositoryQuery query) { super(TITLE, repository, query); setTitle(TITLE); setDescription(DESCRIPTION); } @Override public void applyTo(IRepositoryQuery query) { query.setUrl(getQueryUrl(getTaskRepository().getRepositoryUrl())); query.setSummary(getQueryTitle()); } public void createControl(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayoutData(new GridData()); composite.setLayout(new GridLayout(1, false)); createTitleGroup(composite); ScrolledComposite scrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL | SWT.BORDER); GridData gd = new GridData(GridData.FILL, GridData.FILL, true, true); scrolledComposite.setExpandHorizontal(true); scrolledComposite.setLayoutData(gd); scrollComposite = new Composite(scrolledComposite, SWT.None); scrolledComposite.setContent(scrollComposite); scrollComposite.setLayout(new GridLayout(4, false)); createAddFilterGroup(composite); if (getQuery() != null) { titleText.setText(getQuery().getSummary()); TracSearch search = TracUtil.toTracSearch(getQuery()); if (search != null) { restoreWidgetValues(search); } } Dialog.applyDialogFont(composite); setControl(composite); } @Override public String getQueryTitle() { return (titleText != null) ? titleText.getText() : null; } private void restoreWidgetValues(TracSearch search) { List<TracSearchFilter> filters = search.getFilters(); for (TracSearchFilter filter : filters) { SearchField field = getSearchField(filter.getFieldName()); if (field != null) { showSearchField(field, filter); } else { StatusHandler.log(new Status(IStatus.WARNING, TracUiPlugin.ID_PLUGIN, "Ignoring invalid search filter: " + filter)); //$NON-NLS-1$ } } } private SearchField getSearchField(String fieldName) { for (SearchField searchField : searchFields) { if (searchField.getFieldName().equals(fieldName)) { return searchField; } } return null; } private void createAddFilterGroup(Composite parent) { GridLayout layout; GridData gd; Composite composite = new Composite(parent, SWT.NONE); layout = new GridLayout(2, false); composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); gd = new GridData(GridData.BEGINNING | GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL); composite.setLayoutData(gd); Label label = new Label(composite, SWT.LEFT); label.setText(Messages.TracFilterQueryPage_Select_to_add_filter); // condition final Combo filterCombo = new Combo(composite, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); searchFields = new ArrayList<SearchField>(); searchFields.add(new TextSearchField("summary", Messages.TracFilterQueryPage_Summary)); //$NON-NLS-1$ searchFields.add(new TextSearchField("reporter", Messages.TracFilterQueryPage_Reporter)); //$NON-NLS-1$ searchFields.add(new TextSearchField("owner", Messages.TracFilterQueryPage_Owner)); //$NON-NLS-1$ searchFields.add(new TextSearchField("type", Messages.TracFilterQueryPage_Type)); //$NON-NLS-1$ searchFields.add(new TextSearchField("status", Messages.TracFilterQueryPage_Status)); //$NON-NLS-1$ searchFields.add(new TextSearchField("priority", Messages.TracFilterQueryPage_Priority)); //$NON-NLS-1$ searchFields.add(new TextSearchField("milestone", Messages.TracFilterQueryPage_Milestone)); //$NON-NLS-1$ searchFields.add(new TextSearchField("component", Messages.TracFilterQueryPage_Component)); //$NON-NLS-1$ searchFields.add(new TextSearchField("version", Messages.TracFilterQueryPage_Version)); //$NON-NLS-1$ searchFields.add(new TextSearchField("resoution", Messages.TracFilterQueryPage_Resolution)); //$NON-NLS-1$ searchFields.add(new TextSearchField("keywords", Messages.TracFilterQueryPage_Keywords)); //$NON-NLS-1$ searchFields.add(new TextSearchField("cc", Messages.TracFilterQueryPage_CC)); //$NON-NLS-1$ filterCombo.add(""); //$NON-NLS-1$ for (SearchField field : searchFields) { filterCombo.add(field.getDisplayName()); } filterCombo.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { if (filterCombo.getText().length() > 0) { SearchField field = searchFields.get(filterCombo.getSelectionIndex() - 1); showSearchField(field, null); filterCombo.setText(""); //$NON-NLS-1$ } } }); } private void createTitleGroup(Composite composite) { Group group = new Group(composite, SWT.NONE); group.setText(TITLE_QUERY_TITLE); group.setLayout(new GridLayout(1, false)); GridData gd = new GridData(GridData.FILL_HORIZONTAL); group.setLayoutData(gd); titleText = new Text(group, SWT.BORDER); gd = new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL); titleText.setLayoutData(gd); titleText.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { // ignore } public void keyReleased(KeyEvent e) { getContainer().updateButtons(); } }); } @Override public boolean isPageComplete() { if (titleText != null && titleText.getText().length() > 0) { return true; } return false; } private void showSearchField(SearchField field, TracSearchFilter filter) { assert filter == null || !visibleSearchFields.contains(field); if (!visibleSearchFields.contains(field)) { field.createControls(scrollComposite, filter); visibleSearchFields.add(field); } else { field.addControl(scrollComposite); } updateScrollPane(); } public String getQueryUrl(String repsitoryUrl) { TracSearch search = new TracSearch(); for (SearchField field : visibleSearchFields) { search.addFilter(field.getFilter()); } StringBuilder sb = new StringBuilder(); sb.append(repsitoryUrl); sb.append(ITracClient.QUERY_URL); sb.append(search.toUrl()); return sb.toString(); } private void hideSearchField(SearchField field) { visibleSearchFields.remove(field); } private void updateScrollPane() { scrollComposite.setSize(scrollComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); scrollComposite.layout(); } private abstract class SearchField { protected String fieldName; private final String displayName; public SearchField(String fieldName, String displayName) { this.fieldName = fieldName; this.displayName = displayName; } public abstract void createControls(Composite parent, TracSearchFilter filter); public abstract void addControl(Composite parent); public String getFieldName() { return fieldName; } public String getDisplayName() { return displayName; } public abstract TracSearchFilter getFilter(); } private class TextSearchField extends SearchField { private final CompareOperator[] compareOperators = { CompareOperator.CONTAINS, CompareOperator.CONTAINS_NOT, CompareOperator.BEGINS_WITH, CompareOperator.ENDS_WITH, CompareOperator.IS, CompareOperator.IS_NOT, }; private List<TextCriterion> criterions; public TextSearchField(String fieldName, String displayName) { super(fieldName, displayName); } @Override public void createControls(Composite parent, TracSearchFilter filter) { if (filter != null) { TextCriterion first = addCriterion(parent); first.setCondition(filter.getOperator()); List<String> values = filter.getValues(); if (!values.isEmpty()) { first.setSearchText(values.get(0)); for (int i = 1; i < values.size(); i++) { TextCriterion criterion = addCriterion(parent); criterion.setSearchText(values.get(1)); } } } else { addCriterion(parent); } } @Override public void addControl(Composite parent) { addCriterion(parent); } public TextCriterion addCriterion(Composite parent) { TextCriterion criterion = new TextCriterion(); if (criterions == null) { criterions = new ArrayList<TextCriterion>(); criterion.createControl(parent); } else { criterion.createControl(parent, criterions.get(criterions.size() - 1)); } criterions.add(criterion); return criterion; } @Override public TracSearchFilter getFilter() { TracSearchFilter newFilter = new TracSearchFilter(getFieldName()); newFilter.setOperator(criterions.get(0).getCondition()); for (TextCriterion criterion : criterions) { newFilter.addValue(criterion.getSearchText()); } return newFilter; } public void removeCriterion(TextCriterion criterion) { int i = criterions.indexOf(criterion); if (i == -1) { throw new RuntimeException(); } if (i == 0) { // the first criterion is special since it contains the compare // operator combo if (criterions.size() > 1) { // copy the value from the second criterion to the first TextCriterion sourceCriterion = criterions.get(1); criterion.searchText.setText(sourceCriterion.searchText.getText()); removeCriterionByIndex(1); } else { // no more criterions, remove all controls removeCriterionByIndex(0); hideSearchField(this); } } else { removeCriterionByIndex(i); } } private void removeCriterionByIndex(int i) { criterions.get(i).remove(); criterions.remove(i); updateScrollPane(); } private class TextCriterion { private Combo conditionCombo; private Text searchText; private Label label; private Button removeButton; public void createControl(Composite parent) { label = new Label(parent, SWT.LEFT); label.setText(getDisplayName() + ": "); //$NON-NLS-1$ conditionCombo = new Combo(parent, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); for (CompareOperator op : compareOperators) { conditionCombo.add(op.toString()); } conditionCombo.setText(compareOperators[0].toString()); createSearchTextAndRemoveButton(parent); } public void createControl(Composite parent, TextCriterion top) { label = new Label(parent, SWT.RIGHT); GridData gd = new GridData(); gd.horizontalAlignment = SWT.END; gd.horizontalSpan = 2; label.setLayoutData(gd); label.setText(Messages.TracFilterQueryPage_or); createSearchTextAndRemoveButton(parent); label.moveBelow(top.removeButton); searchText.moveBelow(label); removeButton.moveBelow(searchText); } private void createSearchTextAndRemoveButton(Composite parent) { searchText = new Text(parent, SWT.BORDER); GridData gd = new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL); searchText.setLayoutData(gd); removeButton = new Button(parent, SWT.PUSH); removeButton.setText("-"); //$NON-NLS-1$ removeButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) { TextSearchField.this.removeCriterion(TextCriterion.this); } }); } public void remove() { label.dispose(); if (conditionCombo != null) { conditionCombo.dispose(); } searchText.dispose(); removeButton.dispose(); } public CompareOperator getCondition() { return (conditionCombo != null) ? compareOperators[conditionCombo.getSelectionIndex()] : null; } public String getSearchText() { return searchText.getText(); } public boolean setCondition(CompareOperator operator) { if (conditionCombo != null) { int i = conditionCombo.indexOf(operator.toString()); if (i != -1) { conditionCombo.select(i); return true; } } return false; } public void setSearchText(String text) { searchText.setText(text); } } } }