/******************************************************************************* * Copyright (c) 2004, 2009 Tasktop Technologies 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: * Tasktop Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.tasks.ui; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.DialogPage; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.mylyn.commons.core.StatusHandler; import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages; import org.eclipse.mylyn.internal.tasks.core.TaskRepositoryManager; import org.eclipse.mylyn.internal.tasks.ui.search.SearchHitCollector; import org.eclipse.mylyn.internal.tasks.ui.views.TaskListView; import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector; import org.eclipse.mylyn.tasks.core.IRepositoryQuery; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.mylyn.tasks.ui.AbstractRepositoryConnectorUi; import org.eclipse.mylyn.tasks.ui.TasksUi; import org.eclipse.mylyn.tasks.ui.TasksUiUtil; import org.eclipse.mylyn.tasks.ui.wizards.AbstractRepositoryQueryPage; import org.eclipse.mylyn.tasks.ui.wizards.ITaskSearchPage; import org.eclipse.mylyn.tasks.ui.wizards.ITaskSearchPageContainer; import org.eclipse.search.internal.ui.SearchPlugin; import org.eclipse.search.ui.ISearchPage; import org.eclipse.search.ui.ISearchPageContainer; import org.eclipse.search.ui.ISearchQuery; import org.eclipse.search.ui.ISearchResultViewPart; import org.eclipse.search.ui.NewSearchUI; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.forms.widgets.ImageHyperlink; /** * @author Rob Elves * @author Mik Kersten * @author Steffen Pingel */ public class TaskSearchPage extends DialogPage implements ISearchPage { public static final String ID = "org.eclipse.mylyn.tasks.ui.search.page"; //$NON-NLS-1$ private static final String PAGE_KEY = "page"; //$NON-NLS-1$ private static final String PAGE_NAME = "TaskSearchPage"; //$NON-NLS-1$ private static final String STORE_REPO_ID = PAGE_NAME + ".REPO"; //$NON-NLS-1$ private Combo repositoryCombo; private Text keyText; private TaskRepository repository; private Composite fParentComposite; private IDialogSettings fDialogSettings; private int currentPageIndex = -1; private boolean firstView = true; private Control[] queryPages; private ISearchPageContainer pageContainer; private ITaskSearchPageContainer taskSearchPageContainer; public boolean performAction() { saveDialogSettings(); String key = keyText.getText(); if (key != null && key.trim().length() > 0) { key = key.trim(); boolean openSuccessful = TasksUiUtil.openTask(repository, key); if (!openSuccessful) { MessageDialog.openInformation(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), Messages.TaskSearchPage_Task_Search, Messages.TaskSearchPage_No_task_found_matching_key_ + key); } return openSuccessful; } else { ITaskSearchPage page = (ITaskSearchPage) queryPages[currentPageIndex].getData(PAGE_KEY); return page.performSearch(); } } public void setContainer(ISearchPageContainer container) { this.pageContainer = container; this.taskSearchPageContainer = new ITaskSearchPageContainer() { public IRunnableContext getRunnableContext() { return pageContainer.getRunnableContext(); } public void setPerformActionEnabled(boolean enabled) { pageContainer.setPerformActionEnabled(enabled); } }; } public void createControl(Composite parent) { fParentComposite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(1, false); // layout.marginHeight = 0; // layout.marginWidth = 0; fParentComposite.setLayout(layout); GridData gd = new GridData(GridData.FILL_BOTH); fParentComposite.setLayoutData(gd); createRepositoryGroup(fParentComposite); createSeparator(fParentComposite); this.setControl(fParentComposite); Dialog.applyDialogFont(fParentComposite); } private void createSeparator(Composite parent) { Label separator = new Label(parent, SWT.NONE); separator.setVisible(false); GridData data = new GridData(GridData.FILL, GridData.FILL, false, false, 2, 1); data.heightHint = convertHeightInCharsToPixels(1) / 3; separator.setLayoutData(data); } private void createRepositoryGroup(Composite control) { Composite group = new Composite(control, SWT.NONE); GridLayout layout = new GridLayout(6, false); group.setLayout(layout); GridData gd = new GridData(GridData.FILL_HORIZONTAL); group.setLayoutData(gd); Label label = new Label(group, SWT.NONE); label.setText(Messages.TaskSearchPage_Repository); repositoryCombo = new Combo(group, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); repositoryCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { displayQueryPage(repositoryCombo.getSelectionIndex()); } }); label = new Label(group, SWT.NONE); label.setText(" "); //$NON-NLS-1$ Label labelKey = new Label(group, SWT.NONE); labelKey.setText(Messages.TaskSearchPage_Task_Key_ID); keyText = new Text(group, SWT.BORDER); keyText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL)); String findText = null; TaskListView taskListView = TaskListView.getFromActivePerspective(); if (taskListView != null) { findText = taskListView.getFilteredTree().getFilterControl().getText(); if (findText != null && findText.trim().length() > 0 && isTaskKeyCandidate(findText.trim())) { pageContainer.setPerformActionEnabled(true); keyText.setText(findText.trim()); keyText.setFocus(); } } keyText.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { // ignore } public void keyReleased(KeyEvent e) { updatePageEnablement(); } }); ImageHyperlink clearKey = new ImageHyperlink(group, SWT.NONE); clearKey.setImage(CommonImages.getImage(CommonImages.REMOVE)); clearKey.addHyperlinkListener(new HyperlinkAdapter() { @Override public void linkActivated(HyperlinkEvent e) { keyText.setText(""); //$NON-NLS-1$ updatePageEnablement(); } }); } private void updatePageEnablement() { if (keyText.getText() != null && keyText.getText().trim().length() > 0) { //setControlsEnabled(queryPages[currentPageIndex], false); if (queryPages != null && queryPages[currentPageIndex] != null && queryPages[currentPageIndex].getData(PAGE_KEY) instanceof AbstractRepositoryQueryPage) { ((AbstractRepositoryQueryPage) queryPages[currentPageIndex].getData(PAGE_KEY)).setControlsEnabled(false); } if (repositoryCombo.getSelectionIndex() > -1) { pageContainer.setPerformActionEnabled(true); } } else { //setControlsEnabled(queryPages[currentPageIndex], true); if (queryPages != null && queryPages[currentPageIndex] != null && queryPages[currentPageIndex].getData(PAGE_KEY) instanceof AbstractRepositoryQueryPage) { ((AbstractRepositoryQueryPage) queryPages[currentPageIndex].getData(PAGE_KEY)).setControlsEnabled(true); } //setControlsEnabled(queryPages[currentPageIndex], true); //pageContainer.setPerformActionEnabled(false); } } private Control createPage(TaskRepository repository, ITaskSearchPage searchPage) { // Page wrapper final Composite pageWrapper = new Composite(fParentComposite, SWT.NONE); pageWrapper.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; pageWrapper.setLayout(layout); try { searchPage.setContainer(taskSearchPageContainer); searchPage.createControl(pageWrapper); } catch (Exception e) { pageWrapper.dispose(); searchPage.dispose(); searchPage = createErrorPage(repository, e); return searchPage.getControl(); } // XXX: work around for initial search page size issue bug#198493 IDialogSettings searchDialogSettings = SearchPlugin.getDefault().getDialogSettingsSection( "DialogBounds_SearchDialog"); //$NON-NLS-1$ if (searchDialogSettings.get("DIALOG_WIDTH") == null) { //$NON-NLS-1$ fParentComposite.getParent().getShell().pack(); } pageWrapper.setData(PAGE_KEY, searchPage); return pageWrapper; } private ITaskSearchPage createErrorPage(TaskRepository repository, Throwable e) { ITaskSearchPage searchPage; Status status = new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, "Error occurred while constructing search page for " + repository.getRepositoryUrl() + " [" //$NON-NLS-1$ //$NON-NLS-2$ + repository.getConnectorKind() + "]", e);//$NON-NLS-1$ StatusHandler.log(status); searchPage = new DeadSearchPage(repository, status); searchPage.setContainer(taskSearchPageContainer); searchPage.createControl(fParentComposite); searchPage.getControl().setData(PAGE_KEY, searchPage); return searchPage; } private void displayQueryPage(final int pageIndex) { if (currentPageIndex == pageIndex || pageIndex < 0) { return; } // TODO: if repository == null display invalid page? if (currentPageIndex != -1 && queryPages[currentPageIndex] != null) { queryPages[currentPageIndex].setVisible(false); ITaskSearchPage page = (ITaskSearchPage) queryPages[currentPageIndex].getData(PAGE_KEY); page.setVisible(false); GridData data = (GridData) queryPages[currentPageIndex].getLayoutData(); data.exclude = true; queryPages[currentPageIndex].setLayoutData(data); } String repositoryLabel = repositoryCombo.getItem(pageIndex); repository = (TaskRepository) repositoryCombo.getData(repositoryLabel); if (queryPages[pageIndex] == null) { if (repository != null) { final AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(repository.getConnectorKind()); if (connectorUi != null) { SafeRunner.run(new ISafeRunnable() { public void run() throws Exception { ITaskSearchPage searchPage = getSearchPage(connectorUi); if (searchPage != null) { queryPages[pageIndex] = createPage(repository, searchPage); } else { AbstractRepositoryConnector connector = TasksUi.getRepositoryManager() .getRepositoryConnector(repository.getConnectorKind()); if (connector.canCreateTaskFromKey(repository)) { queryPages[pageIndex] = createPage(repository, new NoSearchPage(repository)); } } } public void handleException(Throwable e) { ITaskSearchPage page = createErrorPage(repository, e); queryPages[pageIndex] = page.getControl(); } }); } } } // update enablement of the task id field if (repository != null) { AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector( repository.getConnectorKind()); if (connector.canCreateTaskFromKey(repository)) { keyText.setEnabled(true); } else { keyText.setEnabled(false); } } if (queryPages[pageIndex] != null) { GridData data = (GridData) queryPages[pageIndex].getLayoutData(); if (data == null) { data = new GridData(); } data.exclude = false; queryPages[pageIndex].setLayoutData(data); queryPages[pageIndex].setVisible(true); ITaskSearchPage page = (ITaskSearchPage) queryPages[pageIndex].getData(PAGE_KEY); page.setVisible(true); } currentPageIndex = pageIndex; fParentComposite.getParent().layout(true, true); updatePageEnablement(); } private ITaskSearchPage getSearchPage(AbstractRepositoryConnectorUi connectorUi) { if (connectorUi.hasSearchPage()) { return connectorUi.getSearchPage(repository, null); } return null; } @Override public void setVisible(boolean visible) { if (firstView) { firstView = false; getControl().setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true)); List repositories = TasksUi.getRepositoryManager().getAllRepositories(); List searchableRepositories = new ArrayList(); for (TaskRepository repository : repositories) { AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(repository.getConnectorKind()); AbstractRepositoryConnector connector = TasksUi.getRepositoryManager().getRepositoryConnector( repository.getConnectorKind()); if ((connectorUi != null && connectorUi.hasSearchPage() && !repository.isOffline()) || connector.canCreateTaskFromKey(repository)) { searchableRepositories.add(repository); } } Collections.sort(searchableRepositories, new TaskRepositoryComparator()); String[] repositoryUrls = new String[searchableRepositories.size()]; int i = 0; int indexToSelect = 0; for (TaskRepository currRepsitory : searchableRepositories) { if (repository != null && repository.equals(currRepsitory)) { indexToSelect = i; } repositoryUrls[i] = currRepsitory.getRepositoryUrl(); i++; } IDialogSettings settings = getDialogSettings(); if (repositoryCombo != null) { for (int x = 0; x < searchableRepositories.size(); x++) { repositoryCombo.add(searchableRepositories.get(x).getRepositoryLabel()); repositoryCombo.setData(searchableRepositories.get(x).getRepositoryLabel(), searchableRepositories.get(x)); } if (repositoryUrls.length == 0) { MessageDialog.openInformation(Display.getCurrent().getActiveShell(), Messages.TaskSearchPage_Repository_Search, TaskRepositoryManager.MESSAGE_NO_REPOSITORY); } else { String selectRepo = settings.get(STORE_REPO_ID); if (selectRepo != null && repositoryCombo.indexOf(selectRepo) > -1) { repositoryCombo.select(repositoryCombo.indexOf(selectRepo)); repository = (TaskRepository) repositoryCombo.getData(selectRepo); if (repository == null) { // TODO: Display no repository error } } else { repositoryCombo.select(indexToSelect); } // TODO: Create one page per connector and repopulate based on repository queryPages = new Control[repositoryUrls.length]; displayQueryPage(repositoryCombo.getSelectionIndex()); // updateAttributesFromRepository(repositoryCombo.getText(), // null, false); } } } if (queryPages == null) { pageContainer.setPerformActionEnabled(false); } super.setVisible(visible); setDefaultValuesAndFocus(); } private void setDefaultValuesAndFocus() { IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (window != null) { IWorkbenchPage page = window.getActivePage(); if (page != null) { String repositoryUrl = null; IWorkbenchPart part = page.getActivePart(); if (part instanceof ISearchResultViewPart) { ISearchQuery[] queries = NewSearchUI.getQueries(); if (queries.length > 0) { if (queries[0] instanceof SearchHitCollector) { repositoryUrl = ((SearchHitCollector) queries[0]).getRepositoryQuery().getRepositoryUrl(); } } } // if (repositoryUrl == null) { // IEditorPart editor = page.getActiveEditor(); // if (editor instanceof TaskEditor) { // repositoryUrl = ((TaskEditor) editor).getTaskEditorInput().getTask().getRepositoryUrl(); // } // } // if (repositoryUrl == null) { // TaskListView taskListView = TaskListView.getFromActivePerspective(); // if (taskListView != null) { // AbstractTask selectedTask = taskListView.getSelectedTask(); // if (selectedTask != null) { // repositoryUrl = selectedTask.getRepositoryUrl(); // } // } // } if (repositoryUrl != null) { TaskRepository repository = TasksUiPlugin.getRepositoryManager().getRepository(repositoryUrl); if (repository != null) { int index = 0; for (String repositoryLabel : repositoryCombo.getItems()) { if (repositoryLabel.equals(repository.getRepositoryLabel())) { repositoryCombo.select(index); } index++; } displayQueryPage(repositoryCombo.getSelectionIndex()); } } } } if (keyText.getText() != null && keyText.getText().trim().length() > 0) { keyText.setFocus(); keyText.setSelection(0, keyText.getText().length()); } else { Clipboard clipboard = new Clipboard(Display.getDefault()); TextTransfer transfer = TextTransfer.getInstance(); String contents = (String) clipboard.getContents(transfer); if (contents != null) { if (isTaskKeyCandidate(contents.trim())) { keyText.setText(contents.trim()); keyText.setFocus(); keyText.setSelection(0, keyText.getText().length()); } } } updatePageEnablement(); } private boolean isTaskKeyCandidate(String contents) { boolean looksLikeKey = false; try { Integer.parseInt(contents); looksLikeKey = true; } catch (NumberFormatException nfe) { } if (!looksLikeKey) { try { Integer.parseInt(contents.substring(contents.lastIndexOf('-'))); looksLikeKey = true; } catch (Exception e) { } } return looksLikeKey; } public IDialogSettings getDialogSettings() { IDialogSettings settings = TasksUiPlugin.getDefault().getDialogSettings(); fDialogSettings = settings.getSection(PAGE_NAME); if (fDialogSettings == null) { fDialogSettings = settings.addNewSection(PAGE_NAME); } return fDialogSettings; } private void saveDialogSettings() { IDialogSettings settings = getDialogSettings(); settings.put(STORE_REPO_ID, repositoryCombo.getText()); } @Override public void dispose() { if (queryPages != null) { for (Control control : queryPages) { if (control != null) { ITaskSearchPage page = (ITaskSearchPage) control.getData(PAGE_KEY); page.dispose(); } } } super.dispose(); } private class DeadSearchPage extends AbstractRepositoryQueryPage { public DeadSearchPage(TaskRepository rep, Status status) { super("Search page error", rep); //$NON-NLS-1$ } public void createControl(Composite parent) { Hyperlink hyperlink = new Hyperlink(parent, SWT.NONE); hyperlink.setText(Messages.TaskSearchPage_ERROR_Unable_to_present_query_page); hyperlink.setUnderlined(true); hyperlink.addHyperlinkListener(new HyperlinkAdapter() { @Override public void linkActivated(HyperlinkEvent e) { TaskSearchPage.this.getControl().getShell().close(); TasksUiUtil.openEditRepositoryWizard(getTaskRepository()); // TODO: Re-construct this page with new // repository data } }); GridDataFactory.fillDefaults().grab(true, true).applyTo(hyperlink); setControl(hyperlink); } @Override public IRepositoryQuery getQuery() { return null; } @Override public boolean isPageComplete() { return false; } @Override public void setVisible(boolean visible) { super.setVisible(visible); if (visible) { getSearchContainer().setPerformActionEnabled(false); } } @Override public String getQueryTitle() { return null; } @Override public void applyTo(IRepositoryQuery query) { // ignore } } private class NoSearchPage extends AbstractRepositoryQueryPage { public NoSearchPage(TaskRepository rep) { super("No search page", rep); //$NON-NLS-1$ } public void createControl(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout()); Dialog.applyDialogFont(composite); setControl(composite); } @Override public IRepositoryQuery getQuery() { return null; } @Override public boolean isPageComplete() { return false; } @Override public void setVisible(boolean visible) { super.setVisible(visible); if (visible) { getSearchContainer().setPerformActionEnabled(false); } } @Override public String getQueryTitle() { return null; } @Override public void applyTo(IRepositoryQuery query) { // ignore } } }